import gradio as gr import requests import folium import os import json from datetime import datetime, timedelta import pandas as pd # Get API key from environment GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") def create_map(lat=40.7128, lon=-74.0060): """Create a Folium map centered at given coordinates""" m = folium.Map( location=[lat, lon], zoom_start=10, tiles="OpenStreetMap" ) # Add a marker for the selected location folium.Marker( [lat, lon], popup=f"Selected Location: {lat:.4f}, {lon:.4f}", tooltip="Click to select this location", icon=folium.Icon(color='red', icon='info-sign') ).add_to(m) return m._repr_html_() def get_pollen_data(lat, lon, days=5): """Fetch pollen data from Google Pollen API""" if not GOOGLE_API_KEY: return "Error: Google API key not found. Please set GOOGLE_API_KEY as a secret." # Google Pollen API endpoint url = "https://pollen.googleapis.com/v1/forecast:lookup" # Calculate date range start_date = datetime.now() end_date = start_date + timedelta(days=days-1) params = { "key": GOOGLE_API_KEY, "location.longitude": lon, "location.latitude": lat, "days": days, "plantsDescription": True } try: response = requests.get(url, params=params) response.raise_for_status() data = response.json() # Debug: Print the structure to understand the API response print(f"API Response keys: {data.keys()}") if "dailyInfo" in data and len(data["dailyInfo"]) > 0: print(f"First day info keys: {data['dailyInfo'][0].keys()}") print(f"Date structure: {data['dailyInfo'][0].get('date', 'No date field')}") return data except requests.exceptions.RequestException as e: error_msg = f"Error fetching pollen data: {str(e)}" if hasattr(e, 'response') and e.response is not None: error_msg += f" (Status: {e.response.status_code})" return error_msg except json.JSONDecodeError as e: return f"Error parsing response: {str(e)}" except Exception as e: return f"Unexpected error: {str(e)}" def format_pollen_data(data): """Format pollen data for display""" if isinstance(data, str): # Error message return data, None if "dailyInfo" not in data: return "No pollen data available for this location.", None # Create formatted output output = [] output.append("# 🌸 Pollen Forecast Report\n") # Location info if available if "regionInfo" in data: region = data["regionInfo"] output.append(f"**Location:** {region.get('displayName', 'Unknown')}\n") # Daily pollen data daily_data = [] for day_info in data["dailyInfo"]: # Handle different date formats from the API date_info = day_info.get("date", {}) if isinstance(date_info, dict): # If date is a dict, try to extract the date string if "year" in date_info and "month" in date_info and "day" in date_info: year = date_info["year"] month = date_info["month"] day = date_info["day"] date_obj = datetime(year, month, day) else: # Fallback to today's date if we can't parse date_obj = datetime.now() elif isinstance(date_info, str): # If it's already a string, parse it try: date_obj = datetime.strptime(date_info, "%Y-%m-%d") except ValueError: date_obj = datetime.now() else: # Fallback date_obj = datetime.now() formatted_date = date_obj.strftime("%B %d, %Y") output.append(f"## 📅 {formatted_date}\n") if "pollenTypeInfo" in day_info: pollen_types = day_info["pollenTypeInfo"] # Create a row for the dataframe row_data = {"Date": formatted_date} for pollen in pollen_types: pollen_type = pollen["code"].replace("_", " ").title() index_info = pollen.get("indexInfo", {}) index_value = index_info.get("value", "N/A") category = index_info.get("category", "Unknown") # Add color coding based on category color_map = { "VERY_LOW": "🟢", "LOW": "🟡", "MEDIUM": "🟠", "HIGH": "🔴", "VERY_HIGH": "🟣" } color = color_map.get(category, "⚪") output.append(f"**{pollen_type}:** {color} {category} (Index: {index_value})") row_data[pollen_type] = f"{category} ({index_value})" # Add plant descriptions if available if "plantDescription" in pollen: plants = pollen["plantDescription"] if "plants" in plants: plant_list = [plant["displayName"] for plant in plants["plants"][:3]] # Show top 3 output.append(f" - *Main sources: {', '.join(plant_list)}*") output.append("") daily_data.append(row_data) output.append("---\n") # Create DataFrame for tabular view df = None if daily_data: df = pd.DataFrame(daily_data) # Add legend output.append("\n## 📊 Pollen Index Legend") output.append("🟢 Very Low | 🟡 Low | 🟠 Medium | 🔴 High | 🟣 Very High\n") # Add tips output.append("## 💡 Tips") output.append("- Check pollen levels before outdoor activities") output.append("- Take allergy medications during high pollen days") output.append("- Keep windows closed during peak pollen times") output.append("- Shower and change clothes after being outdoors") return "\n".join(output), df def update_location(lat, lon): """Update the map and fetch pollen data for new location""" if lat is None or lon is None: return create_map(), "Please select a location on the map.", None # Create new map new_map = create_map(lat, lon) # Get pollen data pollen_data = get_pollen_data(lat, lon) formatted_data, df = format_pollen_data(pollen_data) return new_map, formatted_data, df # Create the Gradio interface with gr.Blocks(title="🌸 Pollen Forecast Map", theme=gr.themes.Soft()) as app: gr.HTML("""
Click on the map to select a location and get detailed pollen forecasts powered by Google Pollen API