nakas commited on
Commit
9e7f401
Β·
verified Β·
1 Parent(s): 5e0ea57

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +261 -104
app.py CHANGED
@@ -1,134 +1,291 @@
1
  import gradio as gr
2
  import requests
3
- import os
4
  import folium
 
 
 
 
5
 
6
- # Your Google Pollen API key should be set as a secret in your Hugging Face Space
7
- API_KEY = os.environ.get("GOOGLE_API_KEY")
8
-
9
- # --- Map Function ---
10
- def create_map_html(latitude, longitude):
11
- """Creates a Folium map and returns its HTML representation."""
12
- try:
13
- lat = float(latitude)
14
- lon = float(longitude)
15
- # Create a map centered on the coordinates
16
- m = folium.Map(location=[lat, lon], zoom_start=12, tiles="OpenStreetMap")
17
- # Add a marker to the map
18
- folium.Marker([lat, lon], popup="Selected Location").add_to(m)
19
- return m._repr_html_()
20
- except (ValueError, TypeError):
21
- # Return a default map if inputs are invalid or empty
22
- m = folium.Map(location=[40.7128, -74.0060], zoom_start=5, tiles="OpenStreetMap")
23
- return m._repr_html_()
24
-
25
- # --- API Call Function ---
26
- def get_pollen_data(latitude, longitude):
27
- """
28
- Fetches and formats all available pollen data from the Google Pollen API.
29
- Also returns an updated map.
30
- """
31
- if not API_KEY:
32
- error_message = "Error: GOOGLE_API_KEY not found. Please set it as a secret."
33
- map_html = create_map_html(latitude, longitude)
34
- return error_message, error_message, error_message, map_html
35
-
36
- if not latitude or not longitude:
37
- return "", "", "", create_map_html(latitude, longitude)
38
-
39
- try:
40
- lat = float(latitude)
41
- lon = float(longitude)
42
- except (ValueError, TypeError):
43
- error_message = "Error: Invalid latitude or longitude."
44
- return error_message, error_message, error_message, create_map_html(latitude, longitude)
45
 
46
- endpoint = "https://pollen.googleapis.com/v1/forecast:lookup"
47
- params = {"key": API_KEY, "location.latitude": lat, "location.longitude": lon, "days": 5, "languageCode": "en"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  try:
50
- response = requests.get(endpoint, params=params)
51
  response.raise_for_status()
52
  data = response.json()
53
- map_html = create_map_html(latitude, longitude)
54
-
55
- if not data.get('dailyInfo'):
56
- no_data_message = "No pollen data found for this location."
57
- return no_data_message, no_data_message, no_data_message, map_html
58
 
59
- outputs = {'TREE': "", 'GRASS': "", 'WEED': ""}
60
- for day_info in data.get('dailyInfo', []):
61
- date = day_info.get('date', {})
62
- date_str = f"{date.get('year', 'N/A')}-{date.get('month', 'N/A'):02d}-{date.get('day', 'N/A'):02d}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- for cat in outputs.keys():
65
- outputs[cat] += f"## Date: {date_str}\n"
66
-
67
- pollen_found_for_day = {'TREE': False, 'GRASS': False, 'WEED': False}
68
 
69
- for pollen in day_info.get('pollenTypeInfo', []):
70
- plant_type = pollen.get('plantType', 'UNKNOWN')
71
- if plant_type not in outputs: continue
 
 
72
 
73
- pollen_found_for_day[plant_type] = True
 
 
 
 
 
 
 
 
74
 
75
- display_name = pollen.get('displayName', 'N/A')
76
- index_info = pollen.get('indexInfo', {})
77
- category = index_info.get('category', 'N/A')
78
- color = index_info.get('color', '#FFFFFF')
79
- color_swatch = f'<span style="display: inline-block; width: 15px; height: 15px; background-color: {color}; border: 1px solid #ccc; vertical-align: middle;"></span>'
80
 
81
- formatted_pollen_info = f"### {display_name}\n- **Category:** {color_swatch} {category}\n"
82
- outputs[plant_type] += formatted_pollen_info + "\n---\n"
83
-
84
- for p_type, found in pollen_found_for_day.items():
85
- if not found:
86
- outputs[p_type] += "- No specific pollen data returned for this category on this day.\n\n---\n"
 
 
 
 
87
 
88
- return outputs['TREE'].strip(), outputs['GRASS'].strip(), outputs['WEED'].strip(), map_html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- except Exception as e:
91
- error_message = f"An error occurred: {e}"
92
- return error_message, error_message, error_message, create_map_html(latitude, longitude)
 
 
 
 
 
 
 
 
 
 
93
 
94
- # --- Gradio Interface ---
95
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
96
- gr.Markdown("# 🀧 Pollen Forecast Map")
97
- gr.Markdown("Enter coordinates and click 'Get Forecast' to see the 5-day pollen forecast and the location on the map.")
 
 
 
 
98
 
99
  with gr.Row():
100
  with gr.Column(scale=1):
101
- lat_input = gr.Textbox(label="Latitude", value="40.7128")
102
- lon_input = gr.Textbox(label="Longitude", value="-74.0060")
103
- submit_button = gr.Button("Get Forecast", variant="primary")
104
- gr.Examples(
105
- examples=[["34.0522", "-118.2437"], ["51.5074", "-0.1278"]],
106
- inputs=[lat_input, lon_input],
107
- label="Example Locations"
 
 
 
 
 
 
 
108
  )
 
 
 
 
 
 
 
 
 
 
109
 
110
  with gr.Column(scale=2):
111
- map_output = gr.HTML(label="Location Map")
112
-
113
- with gr.Tabs():
114
- with gr.TabItem("🌳 Tree"):
115
- tree_output = gr.Markdown()
116
- with gr.TabItem("🌱 Grass"):
117
- grass_output = gr.Markdown()
118
- with gr.TabItem("🌿 Weed"):
119
- weed_output = gr.Markdown()
120
-
121
- submit_button.click(
122
- fn=get_pollen_data,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  inputs=[lat_input, lon_input],
124
- outputs=[tree_output, grass_output, weed_output, map_output]
125
  )
126
- demo.load(
127
- fn=get_pollen_data,
 
 
 
 
128
  inputs=[lat_input, lon_input],
129
- outputs=[tree_output, grass_output, weed_output, map_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  )
131
 
132
  if __name__ == "__main__":
133
- demo.launch()
134
-
 
1
  import gradio as gr
2
  import requests
 
3
  import folium
4
+ import os
5
+ import json
6
+ from datetime import datetime, timedelta
7
+ import pandas as pd
8
 
9
+ # Get API key from environment
10
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ def create_map(lat=40.7128, lon=-74.0060):
13
+ """Create a Folium map centered at given coordinates"""
14
+ m = folium.Map(
15
+ location=[lat, lon],
16
+ zoom_start=10,
17
+ tiles="OpenStreetMap"
18
+ )
19
+
20
+ # Add a marker for the selected location
21
+ folium.Marker(
22
+ [lat, lon],
23
+ popup=f"Selected Location: {lat:.4f}, {lon:.4f}",
24
+ tooltip="Click to select this location",
25
+ icon=folium.Icon(color='red', icon='info-sign')
26
+ ).add_to(m)
27
+
28
+ return m._repr_html_()
29
 
30
+ def get_pollen_data(lat, lon, days=5):
31
+ """Fetch pollen data from Google Pollen API"""
32
+ if not GOOGLE_API_KEY:
33
+ return "Error: Google API key not found. Please set GOOGLE_API_KEY as a secret."
34
+
35
+ # Google Pollen API endpoint
36
+ url = "https://pollen.googleapis.com/v1/forecast:lookup"
37
+
38
+ # Calculate date range
39
+ start_date = datetime.now()
40
+ end_date = start_date + timedelta(days=days-1)
41
+
42
+ params = {
43
+ "key": GOOGLE_API_KEY,
44
+ "location.longitude": lon,
45
+ "location.latitude": lat,
46
+ "days": days,
47
+ "plantsDescription": True
48
+ }
49
+
50
  try:
51
+ response = requests.get(url, params=params)
52
  response.raise_for_status()
53
  data = response.json()
54
+ return data
55
+ except requests.exceptions.RequestException as e:
56
+ return f"Error fetching pollen data: {str(e)}"
57
+ except json.JSONDecodeError as e:
58
+ return f"Error parsing response: {str(e)}"
59
 
60
+ def format_pollen_data(data):
61
+ """Format pollen data for display"""
62
+ if isinstance(data, str): # Error message
63
+ return data, None
64
+
65
+ if "dailyInfo" not in data:
66
+ return "No pollen data available for this location.", None
67
+
68
+ # Create formatted output
69
+ output = []
70
+ output.append("# 🌸 Pollen Forecast Report\n")
71
+
72
+ # Location info if available
73
+ if "regionInfo" in data:
74
+ region = data["regionInfo"]
75
+ output.append(f"**Location:** {region.get('displayName', 'Unknown')}\n")
76
+
77
+ # Daily pollen data
78
+ daily_data = []
79
+ for day_info in data["dailyInfo"]:
80
+ date = day_info["date"]
81
+ date_obj = datetime.strptime(date, "%Y-%m-%d")
82
+ formatted_date = date_obj.strftime("%B %d, %Y")
83
+
84
+ output.append(f"## πŸ“… {formatted_date}\n")
85
+
86
+ if "pollenTypeInfo" in day_info:
87
+ pollen_types = day_info["pollenTypeInfo"]
88
 
89
+ # Create a row for the dataframe
90
+ row_data = {"Date": formatted_date}
 
 
91
 
92
+ for pollen in pollen_types:
93
+ pollen_type = pollen["code"].replace("_", " ").title()
94
+ index_info = pollen.get("indexInfo", {})
95
+ index_value = index_info.get("value", "N/A")
96
+ category = index_info.get("category", "Unknown")
97
 
98
+ # Add color coding based on category
99
+ color_map = {
100
+ "VERY_LOW": "🟒",
101
+ "LOW": "🟑",
102
+ "MEDIUM": "🟠",
103
+ "HIGH": "πŸ”΄",
104
+ "VERY_HIGH": "🟣"
105
+ }
106
+ color = color_map.get(category, "βšͺ")
107
 
108
+ output.append(f"**{pollen_type}:** {color} {category} (Index: {index_value})")
109
+ row_data[pollen_type] = f"{category} ({index_value})"
 
 
 
110
 
111
+ # Add plant descriptions if available
112
+ if "plantDescription" in pollen:
113
+ plants = pollen["plantDescription"]
114
+ if "plants" in plants:
115
+ plant_list = [plant["displayName"] for plant in plants["plants"][:3]] # Show top 3
116
+ output.append(f" - *Main sources: {', '.join(plant_list)}*")
117
+
118
+ output.append("")
119
+
120
+ daily_data.append(row_data)
121
 
122
+ output.append("---\n")
123
+
124
+ # Create DataFrame for tabular view
125
+ df = None
126
+ if daily_data:
127
+ df = pd.DataFrame(daily_data)
128
+
129
+ # Add legend
130
+ output.append("\n## πŸ“Š Pollen Index Legend")
131
+ output.append("🟒 Very Low | 🟑 Low | 🟠 Medium | πŸ”΄ High | 🟣 Very High\n")
132
+
133
+ # Add tips
134
+ output.append("## πŸ’‘ Tips")
135
+ output.append("- Check pollen levels before outdoor activities")
136
+ output.append("- Take allergy medications during high pollen days")
137
+ output.append("- Keep windows closed during peak pollen times")
138
+ output.append("- Shower and change clothes after being outdoors")
139
+
140
+ return "\n".join(output), df
141
 
142
+ def update_location(lat, lon):
143
+ """Update the map and fetch pollen data for new location"""
144
+ if lat is None or lon is None:
145
+ return create_map(), "Please select a location on the map.", None
146
+
147
+ # Create new map
148
+ new_map = create_map(lat, lon)
149
+
150
+ # Get pollen data
151
+ pollen_data = get_pollen_data(lat, lon)
152
+ formatted_data, df = format_pollen_data(pollen_data)
153
+
154
+ return new_map, formatted_data, df
155
 
156
+ # Create the Gradio interface
157
+ with gr.Blocks(title="🌸 Pollen Forecast Map", theme=gr.themes.Soft()) as app:
158
+ gr.HTML("""
159
+ <div style="text-align: center; padding: 20px;">
160
+ <h1>🌸 Pollen Forecast Map</h1>
161
+ <p>Click on the map to select a location and get detailed pollen forecasts powered by Google Pollen API</p>
162
+ </div>
163
+ """)
164
 
165
  with gr.Row():
166
  with gr.Column(scale=1):
167
+ gr.HTML("<h3>πŸ“ Location Selection</h3>")
168
+
169
+ # Coordinate inputs
170
+ lat_input = gr.Number(
171
+ label="Latitude",
172
+ value=40.7128,
173
+ precision=6,
174
+ info="Enter latitude or click on map"
175
+ )
176
+ lon_input = gr.Number(
177
+ label="Longitude",
178
+ value=-74.0060,
179
+ precision=6,
180
+ info="Enter longitude or click on map"
181
  )
182
+
183
+ update_btn = gr.Button("πŸ”„ Update Location", variant="primary")
184
+
185
+ # Preset locations
186
+ gr.HTML("<h4>πŸ“ Quick Locations</h4>")
187
+ with gr.Row():
188
+ nyc_btn = gr.Button("πŸ™οΈ NYC", size="sm")
189
+ la_btn = gr.Button("🌴 LA", size="sm")
190
+ chicago_btn = gr.Button("🌬️ Chicago", size="sm")
191
+ miami_btn = gr.Button("πŸ–οΈ Miami", size="sm")
192
 
193
  with gr.Column(scale=2):
194
+ # Map display
195
+ map_html = gr.HTML(
196
+ value=create_map(),
197
+ label="Interactive Map"
198
+ )
199
+
200
+ with gr.Row():
201
+ with gr.Column():
202
+ # Pollen data output
203
+ pollen_output = gr.Markdown(
204
+ value="Select a location to view pollen forecast.",
205
+ label="Pollen Forecast"
206
+ )
207
+
208
+ # Data table
209
+ pollen_table = gr.Dataframe(
210
+ label="Pollen Data Summary",
211
+ visible=False
212
+ )
213
+
214
+ # Event handlers
215
+ def set_nyc():
216
+ return 40.7128, -74.0060
217
+
218
+ def set_la():
219
+ return 34.0522, -118.2437
220
+
221
+ def set_chicago():
222
+ return 41.8781, -87.6298
223
+
224
+ def set_miami():
225
+ return 25.7617, -80.1918
226
+
227
+ # Button events
228
+ update_btn.click(
229
+ fn=update_location,
230
  inputs=[lat_input, lon_input],
231
+ outputs=[map_html, pollen_output, pollen_table]
232
  )
233
+
234
+ nyc_btn.click(
235
+ fn=set_nyc,
236
+ outputs=[lat_input, lon_input]
237
+ ).then(
238
+ fn=update_location,
239
  inputs=[lat_input, lon_input],
240
+ outputs=[map_html, pollen_output, pollen_table]
241
+ )
242
+
243
+ la_btn.click(
244
+ fn=set_la,
245
+ outputs=[lat_input, lon_input]
246
+ ).then(
247
+ fn=update_location,
248
+ inputs=[lat_input, lon_input],
249
+ outputs=[map_html, pollen_output, pollen_table]
250
+ )
251
+
252
+ chicago_btn.click(
253
+ fn=set_chicago,
254
+ outputs=[lat_input, lon_input]
255
+ ).then(
256
+ fn=update_location,
257
+ inputs=[lat_input, lon_input],
258
+ outputs=[map_html, pollen_output, pollen_table]
259
+ )
260
+
261
+ miami_btn.click(
262
+ fn=set_miami,
263
+ outputs=[lat_input, lon_input]
264
+ ).then(
265
+ fn=update_location,
266
+ inputs=[lat_input, lon_input],
267
+ outputs=[map_html, pollen_output, pollen_table]
268
+ )
269
+
270
+ # Auto-update when coordinates change
271
+ lat_input.change(
272
+ fn=update_location,
273
+ inputs=[lat_input, lon_input],
274
+ outputs=[map_html, pollen_output, pollen_table]
275
+ )
276
+
277
+ lon_input.change(
278
+ fn=update_location,
279
+ inputs=[lat_input, lon_input],
280
+ outputs=[map_html, pollen_output, pollen_table]
281
+ )
282
+
283
+ # Load initial data
284
+ app.load(
285
+ fn=update_location,
286
+ inputs=[lat_input, lon_input],
287
+ outputs=[map_html, pollen_output, pollen_table]
288
  )
289
 
290
  if __name__ == "__main__":
291
+ app.launch()