Spaces:
Runtime error
Runtime error
| import io | |
| import requests | |
| import pandas as pd | |
| import matplotlib | |
| matplotlib.use("Agg") # headless backend for Hugging Face / servers | |
| import matplotlib.pyplot as plt | |
| import gradio as gr | |
| GEOCODE_URL = "https://geocoding-api.open-meteo.com/v1/search" | |
| FORECAST_URL = "https://api.open-meteo.com/v1/forecast" | |
| def geocode_city(city: str): | |
| city = (city or "").strip() | |
| if not city: | |
| raise ValueError("Please enter a city name.") | |
| r = requests.get(GEOCODE_URL, params={"name": city, "count": 1, "language": "en"}, timeout=10) | |
| r.raise_for_status() | |
| data = r.json() | |
| if not data.get("results"): | |
| raise ValueError(f"City '{city}' not found.") | |
| res = data["results"][0] | |
| return float(res["latitude"]), float(res["longitude"]), f"{res['name']}, {res.get('country','')}" | |
| def fetch_forecast(lat, lon, days, mode, temp_unit, precip_unit): | |
| params = { | |
| "latitude": lat, | |
| "longitude": lon, | |
| "forecast_days": int(days), | |
| "timezone": "auto", | |
| "temperature_unit": "fahrenheit" if temp_unit == "°F" else "celsius", | |
| "precipitation_unit": "inch" if precip_unit == "in" else "mm", | |
| } | |
| if mode == "Daily": | |
| params["daily"] = "temperature_2m_max,temperature_2m_min,precipitation_sum" | |
| else: | |
| params["hourly"] = "temperature_2m,relativehumidity_2m,precipitation" | |
| r = requests.get(FORECAST_URL, params=params, timeout=15) | |
| r.raise_for_status() | |
| return r.json() | |
| def make_plot(x, ys, labels, title, ylabel): | |
| # ensure x is datetime-like for nice plotting | |
| try: | |
| x = pd.to_datetime(x) | |
| except Exception: | |
| # fall back to plain strings | |
| x = list(map(str, x)) | |
| plt.figure(figsize=(8, 3.5)) | |
| for y, lbl in zip(ys, labels): | |
| plt.plot(x, y, label=lbl) | |
| if labels: | |
| plt.legend() | |
| plt.title(title) | |
| plt.xlabel("Time") | |
| plt.ylabel(ylabel) | |
| plt.tight_layout() | |
| buf = io.BytesIO() | |
| plt.savefig(buf, format="png", dpi=120) | |
| plt.close() | |
| buf.seek(0) | |
| return buf.getvalue() | |
| def run(city, mode, days, temp_unit, precip_unit): | |
| try: | |
| # validate small things | |
| days = int(days) | |
| if days < 1 or days > 14: | |
| raise ValueError("Days must be between 1 and 14.") | |
| lat, lon, place = geocode_city(city) | |
| data = fetch_forecast(lat, lon, days, mode, temp_unit, precip_unit) | |
| if mode == "Daily": | |
| if "daily" not in data: | |
| raise ValueError("Daily data not available for this location.") | |
| d = data["daily"] | |
| df = pd.DataFrame({ | |
| "date": d["time"], | |
| "temp_max": d["temperature_2m_max"], | |
| "temp_min": d["temperature_2m_min"], | |
| "precip": d["precipitation_sum"], | |
| }) | |
| # return types: (markdown, DataFrame, bytes) | |
| img = make_plot(df["date"], [df["temp_max"], df["temp_min"]], | |
| ["Max", "Min"], f"Daily Temperatures — {place}", f"Temp ({temp_unit})") | |
| md = f"### Daily forecast for **{place}**\nNext {days} day(s)\n\nUnits: Temperature **{temp_unit}**, Precipitation **{precip_unit}**" | |
| return md, df, img | |
| else: # Hourly | |
| if "hourly" not in data: | |
| raise ValueError("Hourly data not available for this location.") | |
| h = data["hourly"] | |
| # Ensure we don't slice beyond available length | |
| max_hours = len(h.get("time", [])) | |
| requested_hours = min(days * 24, max_hours) | |
| df = pd.DataFrame({ | |
| "time": h["time"][:requested_hours]_]() | |