import requests # ----------------------------------------------------------- # Geocode a text location (EX: "Boston, MA") using Nominatim # ----------------------------------------------------------- def geocode_location(location): url = "https://nominatim.openstreetmap.org/search" params = { "q": location, "format": "json", "limit": 1 } response = requests.get(url, params=params, timeout=5) data = response.json() if not data: raise ValueError(f"Could not geocode location: {location}") return { "lat": float(data[0]["lat"]), "lng": float(data[0]["lon"]) } # ----------------------------------------------------------- # Estimate user's coordinates using IP geolocation # ----------------------------------------------------------- def estimate_location(): """Estimate user's location using IP-based geolocation.""" try: response = requests.get("https://ipinfo.io/json", timeout=5) data = response.json() if "loc" not in data: raise RuntimeError("IP lookup returned no coordinates.") lat_str, lng_str = data["loc"].split(",") return { "lat": float(lat_str), "lng": float(lng_str) } except Exception as e: raise RuntimeError(f"Could not estimate location: {e}") # ----------------------------------------------------------- # Get the nearest weather office from NOAA API # ----------------------------------------------------------- def get_weather_office(lat, lng): url = f"https://api.weather.gov/points/{lat},{lng}" response = requests.get(url, timeout=5) data = response.json() props = data["properties"] return { "office": props["gridId"], "x": props["gridX"], "y": props["gridY"] } # ----------------------------------------------------------- # Get NOAA forecast # ----------------------------------------------------------- def get_forecast(office, x, y, metric=False): url = f"https://api.weather.gov/gridpoints/{office}/{x},{y}/forecast" response = requests.get(url, timeout=5) data = response.json() periods = data["properties"]["periods"] # Convert NOAA data to your class format forecast = [] for p in periods: forecast.append({ "name": p["name"], "description": p["detailedForecast"], "temperature": p["temperature"] if not metric else round((p["temperature"] - 32) * 5/9), "wind_speed": p["windSpeed"], "wind_direction": p["windDirection"] }) return forecast