From ac633708764551841eec7dd8171d3eeffe5c5a66 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 2 Mar 2026 17:48:45 +0100 Subject: [PATCH] Fix geocoding for "City,Country" format (e.g. Rab,Croatia) Split location on comma, search for city name, then filter results by country name/code to find the correct match. Co-Authored-By: Claude Opus 4.6 --- server/services/weather_service.py | 39 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/server/services/weather_service.py b/server/services/weather_service.py index 8f5a900..9552530 100644 --- a/server/services/weather_service.py +++ b/server/services/weather_service.py @@ -71,17 +71,28 @@ _geocode_cache: Dict[str, Dict[str, Any]] = {} async def _geocode(location: str) -> Optional[Dict[str, Any]]: """Resolve a city name to lat/lon using Open-Meteo Geocoding API. + Supports "City,Country" format (e.g. "Rab,Croatia") — the country part + is used to filter results when multiple matches exist. + Returns dict with keys: latitude, longitude, name, timezone """ cache_key = location.lower().strip() if cache_key in _geocode_cache: return _geocode_cache[cache_key] + # Split "City,Country" into search name + country filter + city_name = location + country_hint = "" + if "," in location: + parts = [p.strip() for p in location.split(",", 1)] + city_name = parts[0] + country_hint = parts[1].lower() if len(parts) > 1 else "" + try: async with httpx.AsyncClient(timeout=10) as client: resp = await client.get( "https://geocoding-api.open-meteo.com/v1/search", - params={"name": location, "count": 1, "language": "de"}, + params={"name": city_name, "count": 10, "language": "de"}, ) resp.raise_for_status() data = resp.json() @@ -94,15 +105,29 @@ async def _geocode(location: str) -> Optional[Dict[str, Any]]: logger.warning("[WEATHER] Geocoding: no results for '%s'", location) return None + # If country hint provided, try to find a matching result + best = results[0] + if country_hint: + for r in results: + country = (r.get("country", "") or "").lower() + country_code = (r.get("country_code", "") or "").lower() + if country_hint in country or country_hint == country_code: + best = r + break + else: + logger.warning("[WEATHER] Country '%s' not found in results for '%s', using first match", + country_hint, city_name) + geo = { - "latitude": results[0]["latitude"], - "longitude": results[0]["longitude"], - "name": results[0].get("name", location), - "timezone": results[0].get("timezone", "Europe/Berlin"), + "latitude": best["latitude"], + "longitude": best["longitude"], + "name": best.get("name", location), + "timezone": best.get("timezone", "Europe/Berlin"), } _geocode_cache[cache_key] = geo - logger.info("[WEATHER] Geocoded '%s' → %s (%.2f, %.2f)", - location, geo["name"], geo["latitude"], geo["longitude"]) + logger.info("[WEATHER] Geocoded '%s' → %s (%s, %.2f, %.2f)", + location, geo["name"], best.get("country", "?"), + geo["latitude"], geo["longitude"]) return geo