Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -96,7 +96,7 @@ def get_pollen_data(lat, lon, days=5):
|
|
96 |
return f"Unexpected error: {str(e)}"
|
97 |
|
98 |
def format_pollen_data(data):
|
99 |
-
"""Format pollen data for display"""
|
100 |
if isinstance(data, str): # Error message
|
101 |
return data, None
|
102 |
|
@@ -110,7 +110,16 @@ def format_pollen_data(data):
|
|
110 |
# Location info if available
|
111 |
if "regionInfo" in data:
|
112 |
region = data["regionInfo"]
|
113 |
-
output.append(f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
# Daily pollen data
|
116 |
daily_data = []
|
@@ -139,43 +148,80 @@ def format_pollen_data(data):
|
|
139 |
date_obj = datetime.now()
|
140 |
|
141 |
formatted_date = date_obj.strftime("%B %d, %Y")
|
|
|
142 |
|
143 |
-
output.append(f"## π
{formatted_date}\n")
|
144 |
|
145 |
if "pollenTypeInfo" in day_info:
|
146 |
pollen_types = day_info["pollenTypeInfo"]
|
147 |
|
148 |
# Create a row for the dataframe
|
149 |
-
row_data = {"Date": formatted_date}
|
|
|
|
|
|
|
|
|
150 |
|
151 |
for pollen in pollen_types:
|
152 |
pollen_type = pollen["code"].replace("_", " ").title()
|
153 |
index_info = pollen.get("indexInfo", {})
|
154 |
-
index_value = index_info.get("value",
|
155 |
category = index_info.get("category", "Unknown")
|
156 |
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
}
|
165 |
-
color = color_map.get(category, "βͺ")
|
166 |
|
167 |
-
|
168 |
-
|
|
|
|
|
|
|
|
|
|
|
169 |
|
170 |
# Add plant descriptions if available
|
171 |
if "plantDescription" in pollen:
|
172 |
plants = pollen["plantDescription"]
|
173 |
if "plants" in plants:
|
174 |
plant_list = [plant["displayName"] for plant in plants["plants"][:3]] # Show top 3
|
175 |
-
output.append(f"
|
176 |
|
177 |
output.append("")
|
178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
daily_data.append(row_data)
|
180 |
|
181 |
output.append("---\n")
|
@@ -185,16 +231,28 @@ def format_pollen_data(data):
|
|
185 |
if daily_data:
|
186 |
df = pd.DataFrame(daily_data)
|
187 |
|
188 |
-
# Add
|
189 |
-
output.append("\n##
|
190 |
-
output.append("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
-
|
193 |
-
output.append("
|
194 |
-
output.append("-
|
195 |
-
output.append("-
|
196 |
-
|
197 |
-
output.append("
|
|
|
|
|
|
|
198 |
|
199 |
return "\n".join(output), df
|
200 |
|
@@ -217,7 +275,8 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
217 |
gr.HTML("""
|
218 |
<div style="text-align: center; padding: 20px;">
|
219 |
<h1>πΈ Pollen Forecast Map</h1>
|
220 |
-
<p>
|
|
|
221 |
</div>
|
222 |
""")
|
223 |
|
@@ -225,35 +284,56 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
225 |
with gr.Column(scale=1):
|
226 |
gr.HTML("<h3>π Location Selection</h3>")
|
227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
# Coordinate inputs
|
229 |
lat_input = gr.Number(
|
230 |
label="Latitude",
|
231 |
value=40.7128,
|
232 |
precision=6,
|
233 |
-
info="Enter latitude
|
234 |
)
|
235 |
lon_input = gr.Number(
|
236 |
label="Longitude",
|
237 |
value=-74.0060,
|
238 |
precision=6,
|
239 |
-
info="Enter longitude
|
240 |
)
|
241 |
|
242 |
-
update_btn = gr.Button("π Update Location", variant="
|
243 |
|
244 |
# Preset locations
|
245 |
-
gr.HTML("<h4
|
|
|
|
|
|
|
246 |
with gr.Row():
|
247 |
-
nyc_btn = gr.Button("ποΈ NYC", size="sm")
|
248 |
-
la_btn = gr.Button("π΄ LA", size="sm")
|
249 |
chicago_btn = gr.Button("π¬οΈ Chicago", size="sm")
|
250 |
miami_btn = gr.Button("ποΈ Miami", size="sm")
|
|
|
|
|
|
|
251 |
|
252 |
with gr.Column(scale=2):
|
253 |
# Map display
|
254 |
map_html = gr.HTML(
|
255 |
value=create_map(),
|
256 |
-
label="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
)
|
258 |
|
259 |
with gr.Row():
|
@@ -271,19 +351,46 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
271 |
)
|
272 |
|
273 |
# Event handlers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
def set_nyc():
|
275 |
-
return 40.7128, -74.0060
|
276 |
|
277 |
def set_la():
|
278 |
-
return 34.0522, -118.2437
|
279 |
|
280 |
def set_chicago():
|
281 |
-
return 41.8781, -87.6298
|
282 |
|
283 |
def set_miami():
|
284 |
-
return 25.7617, -80.1918
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
|
286 |
# Button events
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
update_btn.click(
|
288 |
fn=update_location,
|
289 |
inputs=[lat_input, lon_input],
|
@@ -292,7 +399,7 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
292 |
|
293 |
nyc_btn.click(
|
294 |
fn=set_nyc,
|
295 |
-
outputs=[lat_input, lon_input]
|
296 |
).then(
|
297 |
fn=update_location,
|
298 |
inputs=[lat_input, lon_input],
|
@@ -301,7 +408,7 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
301 |
|
302 |
la_btn.click(
|
303 |
fn=set_la,
|
304 |
-
outputs=[lat_input, lon_input]
|
305 |
).then(
|
306 |
fn=update_location,
|
307 |
inputs=[lat_input, lon_input],
|
@@ -310,7 +417,7 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
310 |
|
311 |
chicago_btn.click(
|
312 |
fn=set_chicago,
|
313 |
-
outputs=[lat_input, lon_input]
|
314 |
).then(
|
315 |
fn=update_location,
|
316 |
inputs=[lat_input, lon_input],
|
@@ -319,7 +426,25 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
319 |
|
320 |
miami_btn.click(
|
321 |
fn=set_miami,
|
322 |
-
outputs=[lat_input, lon_input]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
).then(
|
324 |
fn=update_location,
|
325 |
inputs=[lat_input, lon_input],
|
@@ -341,6 +466,9 @@ with gr.Blocks(title="πΈ Pollen Forecast Map", theme=gr.themes.Soft()) as app:
|
|
341 |
|
342 |
# Load initial data
|
343 |
app.load(
|
|
|
|
|
|
|
344 |
fn=update_location,
|
345 |
inputs=[lat_input, lon_input],
|
346 |
outputs=[map_html, pollen_output, pollen_table]
|
|
|
96 |
return f"Unexpected error: {str(e)}"
|
97 |
|
98 |
def format_pollen_data(data):
|
99 |
+
"""Format pollen data for display in user-friendly terms"""
|
100 |
if isinstance(data, str): # Error message
|
101 |
return data, None
|
102 |
|
|
|
110 |
# Location info if available
|
111 |
if "regionInfo" in data:
|
112 |
region = data["regionInfo"]
|
113 |
+
output.append(f"**π Location:** {region.get('displayName', 'Unknown')}\n")
|
114 |
+
|
115 |
+
# Add explanation of what pollen index means
|
116 |
+
output.append("## π Understanding Your Pollen Forecast\n")
|
117 |
+
output.append("**The Pollen Index** measures pollen concentration in the air:")
|
118 |
+
output.append("- **0-2:** Very Low - Most people won't have symptoms")
|
119 |
+
output.append("- **3-5:** Low - Few people with severe allergies may have mild symptoms")
|
120 |
+
output.append("- **6-7:** Medium - People with allergies will likely have symptoms")
|
121 |
+
output.append("- **8-9:** High - Most people with allergies will have symptoms")
|
122 |
+
output.append("- **10+:** Very High - Everyone with allergies will have severe symptoms\n")
|
123 |
|
124 |
# Daily pollen data
|
125 |
daily_data = []
|
|
|
148 |
date_obj = datetime.now()
|
149 |
|
150 |
formatted_date = date_obj.strftime("%B %d, %Y")
|
151 |
+
day_of_week = date_obj.strftime("%A")
|
152 |
|
153 |
+
output.append(f"## π
{day_of_week}, {formatted_date}\n")
|
154 |
|
155 |
if "pollenTypeInfo" in day_info:
|
156 |
pollen_types = day_info["pollenTypeInfo"]
|
157 |
|
158 |
# Create a row for the dataframe
|
159 |
+
row_data = {"Date": f"{day_of_week}, {formatted_date}"}
|
160 |
+
|
161 |
+
# Calculate overall day severity
|
162 |
+
max_index = 0
|
163 |
+
total_types = 0
|
164 |
|
165 |
for pollen in pollen_types:
|
166 |
pollen_type = pollen["code"].replace("_", " ").title()
|
167 |
index_info = pollen.get("indexInfo", {})
|
168 |
+
index_value = index_info.get("value", 0)
|
169 |
category = index_info.get("category", "Unknown")
|
170 |
|
171 |
+
if isinstance(index_value, (int, float)) and index_value > max_index:
|
172 |
+
max_index = index_value
|
173 |
+
total_types += 1
|
174 |
+
|
175 |
+
# Convert technical terms to user-friendly descriptions
|
176 |
+
severity_map = {
|
177 |
+
"VERY_LOW": {"emoji": "π’", "description": "Excellent", "advice": "Great day to be outdoors!"},
|
178 |
+
"LOW": {"emoji": "π‘", "description": "Good", "advice": "Most people will be fine outdoors"},
|
179 |
+
"MEDIUM": {"emoji": "π ", "description": "Moderate", "advice": "Consider taking allergy medication"},
|
180 |
+
"HIGH": {"emoji": "π΄", "description": "Poor", "advice": "Limit outdoor activities if allergic"},
|
181 |
+
"VERY_HIGH": {"emoji": "π£", "description": "Very Poor", "advice": "Stay indoors if possible"}
|
182 |
+
}
|
183 |
+
|
184 |
+
severity = severity_map.get(category, {"emoji": "βͺ", "description": "Unknown", "advice": "Monitor symptoms"})
|
185 |
+
|
186 |
+
# More specific pollen type descriptions
|
187 |
+
pollen_descriptions = {
|
188 |
+
"Tree": "π³ Tree pollen (oak, birch, cedar, etc.)",
|
189 |
+
"Grass": "π± Grass pollen (timothy, bermuda, etc.)",
|
190 |
+
"Weed": "πΏ Weed pollen (ragweed, sagebrush, etc.)"
|
191 |
}
|
|
|
192 |
|
193 |
+
pollen_desc = pollen_descriptions.get(pollen_type, f"πΈ {pollen_type}")
|
194 |
+
|
195 |
+
output.append(f"### {pollen_desc}")
|
196 |
+
output.append(f"**Level:** {severity['emoji']} {severity['description']} (Index: {index_value}/10)")
|
197 |
+
output.append(f"**What this means:** {severity['advice']}")
|
198 |
+
|
199 |
+
row_data[pollen_type] = f"{severity['description']} ({index_value})"
|
200 |
|
201 |
# Add plant descriptions if available
|
202 |
if "plantDescription" in pollen:
|
203 |
plants = pollen["plantDescription"]
|
204 |
if "plants" in plants:
|
205 |
plant_list = [plant["displayName"] for plant in plants["plants"][:3]] # Show top 3
|
206 |
+
output.append(f"**Main sources:** {', '.join(plant_list)}")
|
207 |
|
208 |
output.append("")
|
209 |
|
210 |
+
# Add overall day assessment
|
211 |
+
if max_index > 0:
|
212 |
+
if max_index <= 2:
|
213 |
+
day_rating = "π’ **Excellent day** for outdoor activities!"
|
214 |
+
elif max_index <= 5:
|
215 |
+
day_rating = "π‘ **Good day** - most people will be comfortable outside"
|
216 |
+
elif max_index <= 7:
|
217 |
+
day_rating = "π **Moderate day** - allergy sufferers should prepare"
|
218 |
+
elif max_index <= 9:
|
219 |
+
day_rating = "π΄ **Challenging day** - limit outdoor exposure if allergic"
|
220 |
+
else:
|
221 |
+
day_rating = "π£ **Very difficult day** - stay indoors if you have allergies"
|
222 |
+
|
223 |
+
output.append(f"**Overall Assessment:** {day_rating}\n")
|
224 |
+
|
225 |
daily_data.append(row_data)
|
226 |
|
227 |
output.append("---\n")
|
|
|
231 |
if daily_data:
|
232 |
df = pd.DataFrame(daily_data)
|
233 |
|
234 |
+
# Add comprehensive advice section
|
235 |
+
output.append("\n## π‘ Practical Tips for Pollen Season")
|
236 |
+
output.append("### π **Indoor Protection:**")
|
237 |
+
output.append("- Keep windows and doors closed during high pollen days")
|
238 |
+
output.append("- Use air conditioning with clean HEPA filters")
|
239 |
+
output.append("- Consider an air purifier for your bedroom")
|
240 |
+
|
241 |
+
output.append("\n### πΆ **When Going Outside:**")
|
242 |
+
output.append("- Check this forecast before planning outdoor activities")
|
243 |
+
output.append("- Wear wraparound sunglasses to protect your eyes")
|
244 |
+
output.append("- Consider wearing a mask during very high pollen days")
|
245 |
+
output.append("- Plan outdoor activities for late evening when pollen counts are lower")
|
246 |
|
247 |
+
output.append("\n### πΏ **After Being Outdoors:**")
|
248 |
+
output.append("- Shower and change clothes to remove pollen")
|
249 |
+
output.append("- Wash your hair before bed to avoid pollen on your pillow")
|
250 |
+
output.append("- Keep pets indoors or wipe them down after walks")
|
251 |
+
|
252 |
+
output.append("\n### π **Medication Timing:**")
|
253 |
+
output.append("- Start allergy medications BEFORE symptoms begin")
|
254 |
+
output.append("- Take antihistamines in the evening for next-day protection")
|
255 |
+
output.append("- Consult your doctor about prescription options for severe allergies")
|
256 |
|
257 |
return "\n".join(output), df
|
258 |
|
|
|
275 |
gr.HTML("""
|
276 |
<div style="text-align: center; padding: 20px;">
|
277 |
<h1>πΈ Pollen Forecast Map</h1>
|
278 |
+
<p>Get detailed, easy-to-understand pollen forecasts for any location worldwide!</p>
|
279 |
+
<p><strong>π‘ How to use:</strong> Search by address, use preset cities, or enter coordinates manually</p>
|
280 |
</div>
|
281 |
""")
|
282 |
|
|
|
284 |
with gr.Column(scale=1):
|
285 |
gr.HTML("<h3>π Location Selection</h3>")
|
286 |
|
287 |
+
# Address search
|
288 |
+
gr.HTML("<h4>π Search by Address</h4>")
|
289 |
+
address_input = gr.Textbox(
|
290 |
+
label="Enter Address or City",
|
291 |
+
placeholder="e.g., Central Park, New York or Paris, France",
|
292 |
+
info="Enter any address, city, or landmark"
|
293 |
+
)
|
294 |
+
search_btn = gr.Button("π Search Address", variant="primary")
|
295 |
+
|
296 |
+
gr.HTML("<h4>π Or Use Coordinates</h4>")
|
297 |
# Coordinate inputs
|
298 |
lat_input = gr.Number(
|
299 |
label="Latitude",
|
300 |
value=40.7128,
|
301 |
precision=6,
|
302 |
+
info="Enter latitude (-90 to 90)"
|
303 |
)
|
304 |
lon_input = gr.Number(
|
305 |
label="Longitude",
|
306 |
value=-74.0060,
|
307 |
precision=6,
|
308 |
+
info="Enter longitude (-180 to 180)"
|
309 |
)
|
310 |
|
311 |
+
update_btn = gr.Button("π Update Location", variant="secondary")
|
312 |
|
313 |
# Preset locations
|
314 |
+
gr.HTML("<h4>ποΈ Popular Cities</h4>")
|
315 |
+
with gr.Row():
|
316 |
+
nyc_btn = gr.Button("π½ New York", size="sm")
|
317 |
+
la_btn = gr.Button("π΄ Los Angeles", size="sm")
|
318 |
with gr.Row():
|
|
|
|
|
319 |
chicago_btn = gr.Button("π¬οΈ Chicago", size="sm")
|
320 |
miami_btn = gr.Button("ποΈ Miami", size="sm")
|
321 |
+
with gr.Row():
|
322 |
+
london_btn = gr.Button("π¬π§ London", size="sm")
|
323 |
+
tokyo_btn = gr.Button("π―π΅ Tokyo", size="sm")
|
324 |
|
325 |
with gr.Column(scale=2):
|
326 |
# Map display
|
327 |
map_html = gr.HTML(
|
328 |
value=create_map(),
|
329 |
+
label="π Current Location"
|
330 |
+
)
|
331 |
+
|
332 |
+
# Location info display
|
333 |
+
location_info = gr.Textbox(
|
334 |
+
label="π Selected Location",
|
335 |
+
value="New York City, NY, USA",
|
336 |
+
interactive=False
|
337 |
)
|
338 |
|
339 |
with gr.Row():
|
|
|
351 |
)
|
352 |
|
353 |
# Event handlers
|
354 |
+
def search_address(address):
|
355 |
+
"""Search for address and return coordinates"""
|
356 |
+
if not address.strip():
|
357 |
+
return 40.7128, -74.0060, "Please enter an address to search"
|
358 |
+
|
359 |
+
lat, lon, display_name = geocode_address(address)
|
360 |
+
if lat is not None and lon is not None:
|
361 |
+
return lat, lon, display_name
|
362 |
+
else:
|
363 |
+
return 40.7128, -74.0060, display_name # display_name contains error message
|
364 |
+
|
365 |
def set_nyc():
|
366 |
+
return 40.7128, -74.0060, "New York City, NY, USA"
|
367 |
|
368 |
def set_la():
|
369 |
+
return 34.0522, -118.2437, "Los Angeles, CA, USA"
|
370 |
|
371 |
def set_chicago():
|
372 |
+
return 41.8781, -87.6298, "Chicago, IL, USA"
|
373 |
|
374 |
def set_miami():
|
375 |
+
return 25.7617, -80.1918, "Miami, FL, USA"
|
376 |
+
|
377 |
+
def set_london():
|
378 |
+
return 51.5074, -0.1278, "London, England, UK"
|
379 |
+
|
380 |
+
def set_tokyo():
|
381 |
+
return 35.6762, 139.6503, "Tokyo, Japan"
|
382 |
|
383 |
# Button events
|
384 |
+
search_btn.click(
|
385 |
+
fn=search_address,
|
386 |
+
inputs=[address_input],
|
387 |
+
outputs=[lat_input, lon_input, location_info]
|
388 |
+
).then(
|
389 |
+
fn=update_location,
|
390 |
+
inputs=[lat_input, lon_input],
|
391 |
+
outputs=[map_html, pollen_output, pollen_table]
|
392 |
+
)
|
393 |
+
|
394 |
update_btn.click(
|
395 |
fn=update_location,
|
396 |
inputs=[lat_input, lon_input],
|
|
|
399 |
|
400 |
nyc_btn.click(
|
401 |
fn=set_nyc,
|
402 |
+
outputs=[lat_input, lon_input, location_info]
|
403 |
).then(
|
404 |
fn=update_location,
|
405 |
inputs=[lat_input, lon_input],
|
|
|
408 |
|
409 |
la_btn.click(
|
410 |
fn=set_la,
|
411 |
+
outputs=[lat_input, lon_input, location_info]
|
412 |
).then(
|
413 |
fn=update_location,
|
414 |
inputs=[lat_input, lon_input],
|
|
|
417 |
|
418 |
chicago_btn.click(
|
419 |
fn=set_chicago,
|
420 |
+
outputs=[lat_input, lon_input, location_info]
|
421 |
).then(
|
422 |
fn=update_location,
|
423 |
inputs=[lat_input, lon_input],
|
|
|
426 |
|
427 |
miami_btn.click(
|
428 |
fn=set_miami,
|
429 |
+
outputs=[lat_input, lon_input, location_info]
|
430 |
+
).then(
|
431 |
+
fn=update_location,
|
432 |
+
inputs=[lat_input, lon_input],
|
433 |
+
outputs=[map_html, pollen_output, pollen_table]
|
434 |
+
)
|
435 |
+
|
436 |
+
london_btn.click(
|
437 |
+
fn=set_london,
|
438 |
+
outputs=[lat_input, lon_input, location_info]
|
439 |
+
).then(
|
440 |
+
fn=update_location,
|
441 |
+
inputs=[lat_input, lon_input],
|
442 |
+
outputs=[map_html, pollen_output, pollen_table]
|
443 |
+
)
|
444 |
+
|
445 |
+
tokyo_btn.click(
|
446 |
+
fn=set_tokyo,
|
447 |
+
outputs=[lat_input, lon_input, location_info]
|
448 |
).then(
|
449 |
fn=update_location,
|
450 |
inputs=[lat_input, lon_input],
|
|
|
466 |
|
467 |
# Load initial data
|
468 |
app.load(
|
469 |
+
fn=lambda: (40.7128, -74.0060, "New York City, NY, USA"),
|
470 |
+
outputs=[lat_input, lon_input, location_info]
|
471 |
+
).then(
|
472 |
fn=update_location,
|
473 |
inputs=[lat_input, lon_input],
|
474 |
outputs=[map_html, pollen_output, pollen_table]
|