114 lines
4.3 KiB
TypeScript
114 lines
4.3 KiB
TypeScript
import { normalizeImgwCurrentWeather } from "@/lib/imgw-current-api";
|
|
import { toNumber } from "@/lib/weather-utils";
|
|
import type { CurrentWeatherCondition, ImgwCurrentWeather, RawImgwHybridWeatherResponse } from "@/types/imgw-current";
|
|
import type { WeatherRegion } from "@/types/weather-region";
|
|
|
|
const IMGW_HYBRID_URL = "https://meteo.imgw.pl/api/v1/forecast/fcapi";
|
|
// This browser token is published by the official meteo.imgw.pl frontend.
|
|
const IMGW_HYBRID_TOKEN = "p4DXKjsYadfBV21TYrDk";
|
|
const OPEN_METEO_FORECAST_URL = "https://api.open-meteo.com/v1/forecast";
|
|
|
|
interface RawOpenMeteoCurrentResponse {
|
|
current?: {
|
|
time?: string;
|
|
temperature_2m?: unknown;
|
|
apparent_temperature?: unknown;
|
|
relative_humidity_2m?: unknown;
|
|
precipitation?: unknown;
|
|
rain?: unknown;
|
|
showers?: unknown;
|
|
snowfall?: unknown;
|
|
weather_code?: unknown;
|
|
cloud_cover?: unknown;
|
|
pressure_msl?: unknown;
|
|
wind_speed_10m?: unknown;
|
|
wind_direction_10m?: unknown;
|
|
};
|
|
}
|
|
|
|
function getOpenMeteoCondition(weatherCode: number | null, rain: number | null, showers: number | null, snowfall: number | null): CurrentWeatherCondition {
|
|
if (weatherCode !== null && weatherCode >= 95) return "thunderstorm";
|
|
if ((snowfall ?? 0) > 0 || (weatherCode !== null && weatherCode >= 71 && weatherCode <= 77)) return "snow";
|
|
if ((rain ?? 0) > 0 || (showers ?? 0) > 0 || (weatherCode !== null && weatherCode >= 51 && weatherCode <= 67) || (weatherCode !== null && weatherCode >= 80 && weatherCode <= 82)) return "rain";
|
|
return null;
|
|
}
|
|
|
|
function normalizeOpenMeteoCurrent(payload: RawOpenMeteoCurrentResponse): ImgwCurrentWeather | null {
|
|
const current = payload.current;
|
|
if (!current?.time) return null;
|
|
const measuredAt = new Date(current.time).toISOString();
|
|
const weatherCode = toNumber(current.weather_code);
|
|
const rain = toNumber(current.rain);
|
|
const showers = toNumber(current.showers);
|
|
const snowfall = toNumber(current.snowfall);
|
|
const precipitation = toNumber(current.precipitation);
|
|
|
|
return {
|
|
coverage: "global-model",
|
|
region: "GLOBAL",
|
|
source: "OPEN_METEO",
|
|
sourceLabel: "Open-Meteo",
|
|
sourceType: "model",
|
|
isOfficial: false,
|
|
fetchedAt: new Date().toISOString(),
|
|
measuredAt,
|
|
temperature: toNumber(current.temperature_2m),
|
|
feelsLike: toNumber(current.apparent_temperature),
|
|
windSpeed: toNumber(current.wind_speed_10m),
|
|
windDirection: toNumber(current.wind_direction_10m),
|
|
humidity: toNumber(current.relative_humidity_2m),
|
|
pressure: toNumber(current.pressure_msl),
|
|
precipitation10m: precipitation,
|
|
rainfall10m: rain ?? showers ?? precipitation,
|
|
snowfall10m: snowfall,
|
|
cloudCover: toNumber(current.cloud_cover),
|
|
weatherCode,
|
|
condition: getOpenMeteoCondition(weatherCode, rain, showers, snowfall),
|
|
};
|
|
}
|
|
|
|
export async function fetchImgwHybridCurrentWeather(latitude: number, longitude: number) {
|
|
const params = new URLSearchParams({
|
|
token: IMGW_HYBRID_TOKEN,
|
|
lat: String(latitude),
|
|
lon: String(longitude),
|
|
m: "hybrid",
|
|
});
|
|
const response = await fetch(`${IMGW_HYBRID_URL}?${params}`, { next: { revalidate: 120 } });
|
|
if (!response.ok) throw new Error("IMGW Hybrid service is unavailable.");
|
|
return await response.json() as RawImgwHybridWeatherResponse;
|
|
}
|
|
|
|
export async function fetchServerCurrentWeather(latitude: number, longitude: number, region: WeatherRegion) {
|
|
if (region === "PL") {
|
|
return normalizeImgwCurrentWeather(await fetchImgwHybridCurrentWeather(latitude, longitude));
|
|
}
|
|
|
|
const params = new URLSearchParams({
|
|
latitude: String(latitude),
|
|
longitude: String(longitude),
|
|
current: [
|
|
"temperature_2m",
|
|
"apparent_temperature",
|
|
"relative_humidity_2m",
|
|
"precipitation",
|
|
"rain",
|
|
"showers",
|
|
"snowfall",
|
|
"weather_code",
|
|
"cloud_cover",
|
|
"pressure_msl",
|
|
"wind_speed_10m",
|
|
"wind_direction_10m",
|
|
].join(","),
|
|
timezone: "auto",
|
|
wind_speed_unit: "ms",
|
|
precipitation_unit: "mm",
|
|
timeformat: "iso8601",
|
|
forecast_days: "1",
|
|
});
|
|
const response = await fetch(`${OPEN_METEO_FORECAST_URL}?${params}`, { next: { revalidate: 120 } });
|
|
if (!response.ok) throw new Error("Open-Meteo current conditions are unavailable.");
|
|
return normalizeOpenMeteoCurrent(await response.json() as RawOpenMeteoCurrentResponse);
|
|
}
|