Umar4321's picture
Update app.py
268cea4 verified
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]_]()