Files
wtr/lib/location-utils.ts

67 lines
2.6 KiB
TypeScript

import type { MeteoStationPosition, SynopStation } from "@/types/imgw";
import type { LocationSearchResult, SelectedLocation } from "@/types/location";
const stationAliases: Record<string, string> = {
gdansk: "gdanskrebiechowo",
gorzow: "gorzowwielkopolski",
katowice: "katowicemuchowiec",
kielce: "kielcesukow",
kolo: "koloradoszewice",
kolobrzeg: "kolobrzegdzwirzyno",
krakow: "krakowbalice",
lublin: "lublinradawiec",
lodz: "lodzlublinek",
poznan: "poznanlawica",
resko: "reskosmolsko",
rzeszow: "rzeszowjasionka",
warszawa: "warszawaokecie",
};
function normalizeName(value: string) {
return value
.replace(/[Łł]/g, "l")
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/[^a-zA-Z0-9]/g, "")
.toLowerCase();
}
function distanceKm(latitudeA: number, longitudeA: number, latitudeB: number, longitudeB: number) {
const earthRadiusKm = 6371;
const toRadians = (value: number) => value * Math.PI / 180;
const latitudeDistance = toRadians(latitudeB - latitudeA);
const longitudeDistance = toRadians(longitudeB - longitudeA);
const a = Math.sin(latitudeDistance / 2) ** 2 +
Math.cos(toRadians(latitudeA)) * Math.cos(toRadians(latitudeB)) * Math.sin(longitudeDistance / 2) ** 2;
return earthRadiusKm * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
export interface LocatedSynopStation extends SynopStation {
latitude: number;
longitude: number;
}
export function locateSynopStations(stations: SynopStation[], positions: MeteoStationPosition[]) {
const positionsByName = new Map(positions.map((position) => [normalizeName(position.name), position]));
return stations.flatMap<LocatedSynopStation>((station) => {
const normalizedStation = normalizeName(station.name);
const position = positionsByName.get(stationAliases[normalizedStation] ?? normalizedStation);
return position ? [{ ...station, latitude: position.latitude, longitude: position.longitude }] : [];
});
}
export function findNearestSynopStation(location: LocationSearchResult, stations: LocatedSynopStation[]): SelectedLocation | null {
const nearest = stations.reduce<{ station: LocatedSynopStation; distanceKm: number } | null>((best, station) => {
const distance = distanceKm(location.latitude, location.longitude, station.latitude, station.longitude);
return !best || distance < best.distanceKm ? { station, distanceKm: distance } : best;
}, null);
if (!nearest) return null;
return {
name: location.name,
province: location.province,
stationId: nearest.station.id,
stationName: nearest.station.name,
distanceKm: Math.round(nearest.distanceKm),
};
}