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 { const key = process.env.OPENWEATHER_API_KEY?.trim(); const q = encodeURIComponent(query.slice(0, 120)); if (key) { const places = await fetchJson(`${OWM_GEO}/direct?q=${q}&limit=5&appid=${key}`, "OpenWeather"); return places.map(fromOwm); } const data = await fetchJson( `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 { const fallback: Place = { name: "Your location", lat, lon }; const key = process.env.OPENWEATHER_API_KEY?.trim(); if (!key) return fallback; const places = await fetchJson(`${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; }