Umar4321 commited on
Commit
9731ab0
·
verified ·
1 Parent(s): 51c9c1d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -195
app.py CHANGED
@@ -1,105 +1,107 @@
1
  import io
 
 
2
  import requests
3
  import pandas as pd
 
 
 
 
4
  import matplotlib.pyplot as plt
5
- import datetime
6
  import gradio as gr
7
 
8
  GEOCODE_URL = "https://geocoding-api.open-meteo.com/v1/search"
9
  FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
10
 
11
 
12
- def geocode_city(city_name):
13
- params = {"name": city_name, "count": 1, "language": "en"}
14
- r = requests.get(GEOCODE_URL, params=params, timeout=10)
 
 
 
15
  r.raise_for_status()
16
  data = r.json()
17
- if "results" not in data or len(data["results"]) == 0:
18
  raise ValueError(f"Location not found: '{city_name}'")
19
  res = data["results"][0]
20
  return {
21
- "name": res.get("name"),
22
- "country": res.get("country"),
23
- "latitude": res["latitude"],
24
- "longitude": res["longitude"],
25
- "timezone": res.get("timezone", "UTC"),
26
  }
27
 
28
 
29
- def fetch_forecast(lat, lon, days, forecast_type, temp_unit, precip_unit, timezone):
30
  params = {
31
  "latitude": lat,
32
  "longitude": lon,
33
- "timezone": "auto", # ask API to return local times automatically
34
  "forecast_days": int(days),
35
- # Use sensible hourly/daily params depending on forecast_type
 
 
36
  }
37
 
38
- if forecast_type == "Daily":
39
- params["daily"] = ",".join(
40
- [
41
- "temperature_2m_max",
42
- "temperature_2m_min",
43
- "precipitation_sum",
44
- "sunrise",
45
- "sunset",
46
- ]
47
- )
48
  else: # Hourly
49
- params["hourly"] = ",".join(
50
- [
51
- "temperature_2m",
52
- "relativehumidity_2m",
53
- "apparent_temperature",
54
- "precipitation",
55
- "windspeed_10m",
56
- ]
57
- )
58
-
59
- # unit settings supported by Open-Meteo
60
- params["temperature_unit"] = "fahrenheit" if temp_unit == "°F" else "celsius"
61
- params["precipitation_unit"] = "inch" if precip_unit == "in" else "mm"
62
-
63
- r = requests.get(FORECAST_URL, params=params, timeout=10)
64
  r.raise_for_status()
65
  return r.json()
66
 
67
 
68
- def build_daily_df(daily):
69
- df = pd.DataFrame(
70
- {
71
- "date": daily["time"],
72
- "temp_max": daily.get("temperature_2m_max"),
73
- "temp_min": daily.get("temperature_2m_min"),
74
- "precipitation": daily.get("precipitation_sum"),
75
- "sunrise": daily.get("sunrise"),
76
- "sunset": daily.get("sunset"),
77
- }
78
- )
79
- df["date"] = pd.to_datetime(df["date"]).dt.date
80
  return df
81
 
82
 
83
- def build_hourly_df(hourly, days):
84
- times = pd.to_datetime(hourly["time"])
85
- limit = int(days) * 24
86
- df = pd.DataFrame(
87
- {
88
- "time": times,
89
- "temperature": hourly.get("temperature_2m"),
90
- "apparent_temperature": hourly.get("apparent_temperature"),
91
- "humidity": hourly.get("relativehumidity_2m"),
92
- "precipitation": hourly.get("precipitation"),
93
- "wind_speed": hourly.get("windspeed_10m"),
94
- }
95
- )
96
- df = df.iloc[:limit].copy()
97
  return df
98
 
99
 
100
- def plot_series(x, y, title, xlabel="Time", ylabel=""):
101
- plt.figure(figsize=(9, 3.5))
102
- plt.plot(x, y)
 
 
 
103
  plt.title(title)
104
  plt.xlabel(xlabel)
105
  plt.ylabel(ylabel)
@@ -107,131 +109,4 @@ def plot_series(x, y, title, xlabel="Time", ylabel=""):
107
  buf = io.BytesIO()
108
  plt.savefig(buf, format="png", dpi=150)
109
  plt.close()
110
- buf.seek(0)
111
- return buf
112
-
113
-
114
- def get_weather_forecast(
115
- city,
116
- forecast_type,
117
- days,
118
- temp_unit,
119
- precip_unit,
120
- ):
121
- """
122
- Main function that Gradio will call.
123
- Returns: (markdown_summary, pandas.DataFrame, image_bytes)
124
- """
125
- try:
126
- # 1) Geocode the city
127
- loc = geocode_city(city)
128
- lat = loc["latitude"]
129
- lon = loc["longitude"]
130
- display_name = f"{loc['name']}, {loc.get('country', '')} (lat {lat:.4f}, lon {lon:.4f})"
131
-
132
- # 2) Fetch forecast
133
- resp = fetch_forecast(lat, lon, days, forecast_type, temp_unit, precip_unit, loc["timezone"])
134
-
135
- # 3) Build dataframe and plot
136
- if forecast_type == "Daily":
137
- if "daily" not in resp:
138
- raise ValueError("Daily forecast data not available for this location.")
139
- df = build_daily_df(resp["daily"])
140
- # build a simple summary markdown
141
- first = df.iloc[0]
142
- summary = (
143
- f"### Weather forecast for **{display_name}** — next {days} day(s)\n\n"
144
- f"- **Today (first day)** — Max: {first['temp_max']} {temp_unit}, "
145
- f"Min: {first['temp_min']} {temp_unit}, Precipitation: {first['precipitation']} {precip_unit}\n\n"
146
- "Below is the daily table and a plot of daily high/low temperature."
147
- )
148
- # Plot highs and lows
149
- img_buf = plot_series(
150
- x=pd.to_datetime(df["date"]),
151
- y=df["temp_max"],
152
- title=f"Daily max temperature ({temp_unit}) — {loc['name']}",
153
- xlabel="Date",
154
- ylabel=f"Max temp ({temp_unit})",
155
- )
156
- # Add another line for min temp on same chart
157
- # (create combined chart)
158
- plt.figure(figsize=(9, 3.5))
159
- plt.plot(pd.to_datetime(df["date"]), df["temp_max"])
160
- plt.plot(pd.to_datetime(df["date"]), df["temp_min"])
161
- plt.legend(["max", "min"])
162
- plt.title(f"Daily temperatures ({temp_unit}) — {loc['name']}")
163
- plt.xlabel("Date")
164
- plt.ylabel(f"Temperature ({temp_unit})")
165
- plt.tight_layout()
166
- buf = io.BytesIO()
167
- plt.savefig(buf, format="png", dpi=150)
168
- plt.close()
169
- buf.seek(0)
170
- image_bytes = buf.read()
171
-
172
- return summary, df, image_bytes
173
-
174
- else: # Hourly
175
- if "hourly" not in resp:
176
- raise ValueError("Hourly forecast data not available for this location.")
177
- df = build_hourly_df(resp["hourly"], days)
178
- # Simple summary
179
- next_12h = df.iloc[:12]
180
- soon = next_12h.iloc[0]
181
- summary = (
182
- f"### Hourly forecast for **{display_name}** — next {days} day(s) (showing full table)\n\n"
183
- f"- **Next available hour**: {soon['time']} — Temp: {soon['temperature']} {temp_unit}, "
184
- f"Humidity: {soon['humidity']}%.\n\n"
185
- "Below is the hourly table and a temperature time series for the selected period."
186
- )
187
- # Plot temperature vs time (first N hours)
188
- img_buf = plot_series(
189
- x=df["time"].dt.strftime("%Y-%m-%d %H:%M"),
190
- y=df["temperature"],
191
- title=f"Hourly temperature ({temp_unit}) — {loc['name']}",
192
- xlabel="Time",
193
- ylabel=f"Temperature ({temp_unit})",
194
- )
195
- image_bytes = img_buf.getvalue()
196
- return summary, df, image_bytes
197
-
198
- except Exception as e:
199
- # Return the error message in the markdown output; other outputs empty
200
- return f"**Error:** {str(e)}", pd.DataFrame(), None
201
-
202
-
203
- demo_description = """
204
- Simple Weather Forecast app using **Open-Meteo** (no API key required).
205
- - Enter a city name (e.g. "Karachi", "New York", "Tokyo").
206
- - Choose Daily or Hourly forecast and number of days.
207
- - Open-Meteo provides the data; this app geocodes the city automatically.
208
- """
209
-
210
- with gr.Blocks() as demo:
211
- gr.Markdown("# Weather Forecast (Gradio + Open-Meteo)\n" + demo_description)
212
-
213
- with gr.Row():
214
- with gr.Column(scale=2):
215
- city_in = gr.Textbox(label="City name", placeholder="e.g. Karachi or San Francisco", value="Karachi")
216
- forecast_type = gr.Radio(["Daily", "Hourly"], value="Daily", label="Forecast type")
217
- days = gr.Slider(minimum=1, maximum=14, step=1, value=3, label="Days of forecast (1–14)")
218
- temp_unit = gr.Radio(["°C", "°F"], value="°C", label="Temperature unit")
219
- precip_unit = gr.Radio(["mm", "in"], value="mm", label="Precipitation unit")
220
- submit = gr.Button("Get forecast")
221
-
222
- with gr.Column(scale=3):
223
- md_out = gr.Markdown("")
224
- df_out = gr.Dataframe(headers=[""], interactive=False)
225
- img_out = gr.Image(label="Plot", type="bytes")
226
-
227
- def run(city, forecast_type, days, temp_unit, precip_unit):
228
- return get_weather_forecast(city, forecast_type, days, temp_unit, precip_unit)
229
-
230
- submit.click(
231
- run,
232
- inputs=[city_in, forecast_type, days, temp_unit, precip_unit],
233
- outputs=[md_out, df_out, img_out],
234
- )
235
-
236
- if __name__ == "__main__":
237
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import io
2
+ import sys
3
+ import traceback
4
  import requests
5
  import pandas as pd
6
+
7
+ # Use a headless backend for Spaces/servers (prevents runtime errors)
8
+ import matplotlib
9
+ matplotlib.use("Agg")
10
  import matplotlib.pyplot as plt
11
+
12
  import gradio as gr
13
 
14
  GEOCODE_URL = "https://geocoding-api.open-meteo.com/v1/search"
15
  FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
16
 
17
 
18
+ def geocode_city(city_name: str) -> dict:
19
+ if not city_name or not city_name.strip():
20
+ raise ValueError("Please enter a city name.")
21
+
22
+ params = {"name": city_name.strip(), "count": 1, "language": "en"}
23
+ r = requests.get(GEOCODE_URL, params=params, timeout=15)
24
  r.raise_for_status()
25
  data = r.json()
26
+ if not data.get("results"):
27
  raise ValueError(f"Location not found: '{city_name}'")
28
  res = data["results"][0]
29
  return {
30
+ "name": res.get("name", city_name),
31
+ "country": res.get("country", ""),
32
+ "latitude": float(res["latitude"]),
33
+ "longitude": float(res["longitude"]),
34
+ "timezone": res.get("timezone", "auto"),
35
  }
36
 
37
 
38
+ def fetch_forecast(lat: float, lon: float, days: int, mode: str, temp_unit: str, precip_unit: str) -> dict:
39
  params = {
40
  "latitude": lat,
41
  "longitude": lon,
42
+ "timezone": "auto",
43
  "forecast_days": int(days),
44
+ "temperature_unit": "fahrenheit" if temp_unit == "°F" else "celsius",
45
+ "precipitation_unit": "inch" if precip_unit == "in" else "mm",
46
+ "past_days": 0,
47
  }
48
 
49
+ if mode == "Daily":
50
+ params["daily"] = ",".join([
51
+ "temperature_2m_max",
52
+ "temperature_2m_min",
53
+ "precipitation_sum",
54
+ "sunrise",
55
+ "sunset",
56
+ ])
 
 
57
  else: # Hourly
58
+ params["hourly"] = ",".join([
59
+ "temperature_2m",
60
+ "apparent_temperature",
61
+ "relativehumidity_2m",
62
+ "precipitation",
63
+ "windspeed_10m",
64
+ ])
65
+
66
+ r = requests.get(FORECAST_URL, params=params, timeout=20)
 
 
 
 
 
 
67
  r.raise_for_status()
68
  return r.json()
69
 
70
 
71
+ def df_daily(payload: dict) -> pd.DataFrame:
72
+ d = payload["daily"]
73
+ df = pd.DataFrame({
74
+ "date": pd.to_datetime(d["time"]).date,
75
+ "temp_max": d.get("temperature_2m_max"),
76
+ "temp_min": d.get("temperature_2m_min"),
77
+ "precip_total": d.get("precipitation_sum"),
78
+ "sunrise": pd.to_datetime(d.get("sunrise")),
79
+ "sunset": pd.to_datetime(d.get("sunset")),
80
+ })
 
 
81
  return df
82
 
83
 
84
+ def df_hourly(payload: dict, days: int) -> pd.DataFrame:
85
+ h = payload["hourly"]
86
+ df = pd.DataFrame({
87
+ "time": pd.to_datetime(h["time"]),
88
+ "temperature": h.get("temperature_2m"),
89
+ "apparent_temperature": h.get("apparent_temperature"),
90
+ "humidity_%": h.get("relativehumidity_2m"),
91
+ "precip": h.get("precipitation"),
92
+ "wind_speed_10m": h.get("windspeed_10m"),
93
+ })
94
+ # Limit to requested window exactly (API can return more than needed)
95
+ df = df.iloc[: int(days) * 24].copy()
 
 
96
  return df
97
 
98
 
99
+ def make_plot(x, y_list, labels, title, xlabel, ylabel) -> bytes:
100
+ plt.figure(figsize=(9, 3.8))
101
+ for y in y_list:
102
+ plt.plot(x, y)
103
+ if labels:
104
+ plt.legend(labels)
105
  plt.title(title)
106
  plt.xlabel(xlabel)
107
  plt.ylabel(ylabel)
 
109
  buf = io.BytesIO()
110
  plt.savefig(buf, format="png", dpi=150)
111
  plt.close()
112
+ buf.seek(