72 lines
2.7 KiB
TypeScript
72 lines
2.7 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: Pick<LocationSearchResult, "name" | "province" | "latitude" | "longitude">,
|
|
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,
|
|
latitude: location.latitude,
|
|
longitude: location.longitude,
|
|
stationId: nearest.station.id,
|
|
stationName: nearest.station.name,
|
|
distanceKm: Math.round(nearest.distanceKm),
|
|
};
|
|
}
|