init
This commit is contained in:
commit
6250d23297
53 changed files with 6381 additions and 0 deletions
60
lib/weather/geocode.ts
Normal file
60
lib/weather/geocode.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { fetchJson } from "./http";
|
||||
import type { Place } from "./types";
|
||||
|
||||
/**
|
||||
* Turns a city name into coordinates (and coordinates back into a name).
|
||||
*
|
||||
* OpenWeather's geocoder is used when its key is set; otherwise we fall back
|
||||
* to Open-Meteo's free geocoder so search still works with zero keys.
|
||||
*/
|
||||
|
||||
interface OwmPlace {
|
||||
name: string;
|
||||
state?: string;
|
||||
country: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
|
||||
interface OpenMeteoGeocodeResponse {
|
||||
results?: { name: string; admin1?: string; country_code: string; latitude: number; longitude: number }[];
|
||||
}
|
||||
|
||||
const OWM_GEO = "https://api.openweathermap.org/geo/1.0";
|
||||
|
||||
function fromOwm(place: OwmPlace): Place {
|
||||
return { name: place.name, region: place.state, country: place.country, lat: place.lat, lon: place.lon };
|
||||
}
|
||||
|
||||
export async function searchPlaces(query: string): Promise<Place[]> {
|
||||
const key = process.env.OPENWEATHER_API_KEY?.trim();
|
||||
const q = encodeURIComponent(query.slice(0, 120));
|
||||
|
||||
if (key) {
|
||||
const places = await fetchJson<OwmPlace[]>(`${OWM_GEO}/direct?q=${q}&limit=5&appid=${key}`, "OpenWeather");
|
||||
return places.map(fromOwm);
|
||||
}
|
||||
|
||||
const data = await fetchJson<OpenMeteoGeocodeResponse>(
|
||||
`https://geocoding-api.open-meteo.com/v1/search?name=${q}&count=5`,
|
||||
"Open-Meteo",
|
||||
);
|
||||
return (data.results ?? []).map((r) => ({
|
||||
name: r.name,
|
||||
region: r.admin1,
|
||||
country: r.country_code,
|
||||
lat: r.latitude,
|
||||
lon: r.longitude,
|
||||
}));
|
||||
}
|
||||
|
||||
/** Used by "Use my location". Without an OpenWeather key we can't name the spot. */
|
||||
export async function reversePlace(lat: number, lon: number): Promise<Place> {
|
||||
const fallback: Place = { name: "Your location", lat, lon };
|
||||
const key = process.env.OPENWEATHER_API_KEY?.trim();
|
||||
if (!key) return fallback;
|
||||
|
||||
const places = await fetchJson<OwmPlace[]>(`${OWM_GEO}/reverse?lat=${lat}&lon=${lon}&limit=1&appid=${key}`, "OpenWeather");
|
||||
// Keep the user's exact coordinates rather than the city centre's.
|
||||
return places[0] ? { ...fromOwm(places[0]), lat, lon } : fallback;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue