nakas commited on
Commit
8dc2e40
Β·
verified Β·
1 Parent(s): 5f36308

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +312 -128
app.py CHANGED
@@ -14,7 +14,7 @@ class WeatherApp:
14
  self.selected_lon = -98.5795
15
 
16
  def create_map(self):
17
- """Create initial folium map centered on US"""
18
  m = folium.Map(
19
  location=[self.selected_lat, self.selected_lon],
20
  zoom_start=4,
@@ -24,11 +24,42 @@ class WeatherApp:
24
  # Add a marker for the selected location
25
  folium.Marker(
26
  [self.selected_lat, self.selected_lon],
27
- popup=f"Selected Location\nLat: {self.selected_lat:.4f}\nLon: {self.selected_lon:.4f}",
28
  icon=folium.Icon(color='red', icon='info-sign')
29
  ).add_to(m)
30
 
31
- return m._repr_html_()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def update_location(self, lat, lon):
34
  """Update the selected location coordinates"""
@@ -68,47 +99,119 @@ class WeatherApp:
68
  return None, f"Error: {str(e)}"
69
 
70
  def get_uv_index(self, lat, lon):
71
- """Get UV index data (mock implementation - in real app you'd use UV API)"""
72
- # This is a simplified UV calculation based on time of day and season
73
- # In a real app, you'd use a proper UV API like EPA or OpenUV
74
  now = datetime.now()
75
  hour = now.hour
 
 
 
 
 
 
 
76
 
77
- # Simple UV model: peak at noon, varies by season
78
- base_uv = 8 if 3 <= now.month <= 9 else 4 # Higher in spring/summer
79
 
80
  uv_values = []
 
 
81
  for i in range(24):
82
  current_hour = (hour + i) % 24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  if 6 <= current_hour <= 18: # Daylight hours
84
- # Peak UV around noon (12)
85
- uv_factor = 1 - abs(current_hour - 12) / 6
86
- uv = max(0, base_uv * uv_factor)
87
  else:
88
  uv = 0
 
89
  uv_values.append(round(uv, 1))
90
 
91
- return uv_values
92
 
93
- def get_sunscreen_recommendation(self, uv_index):
94
- """Get sunscreen recommendation based on UV index"""
95
- if uv_index <= 2:
96
- return "Low - Minimal protection needed. Wear sunglasses on bright days."
97
- elif uv_index <= 5:
98
- return "Moderate - SPF 15+ sunscreen. Seek shade during midday hours."
99
- elif uv_index <= 7:
100
- return "High - SPF 30+ sunscreen. Reapply every 2 hours. Wear protective clothing."
101
- elif uv_index <= 10:
102
- return "Very High - SPF 50+ sunscreen. Minimize sun exposure 10am-4pm. Wear protective clothing and wide-brimmed hat."
103
- else:
104
- return "Extreme - SPF 50+ sunscreen. Avoid sun exposure. Stay indoors or in shade. Full protective clothing required."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  def create_weather_plot(self):
107
- """Create weather forecast plot with UV index"""
108
  periods, error = self.get_weather_data()
109
 
110
  if error:
111
- # Return empty plot with error message
112
  fig = go.Figure()
113
  fig.add_annotation(
114
  text=f"Error: {error}",
@@ -116,169 +219,251 @@ class WeatherApp:
116
  x=0.5, y=0.5, xanchor='center', yanchor='middle',
117
  showarrow=False, font_size=16
118
  )
119
- fig.update_layout(title="Weather Forecast Error")
120
  return fig, "Error loading weather data"
121
 
122
  # Extract data from periods
123
  times = []
124
  temps = []
125
- humidity = []
126
- wind_speeds = []
127
 
128
- for period in periods:
129
- # Parse time
130
  start_time = datetime.fromisoformat(period['startTime'].replace('Z', '+00:00'))
131
- times.append(start_time.strftime('%m/%d %H:%M'))
132
-
133
- # Temperature
134
  temps.append(period['temperature'])
135
-
136
- # Try to extract humidity and wind (may not always be available)
137
- try:
138
- # Humidity is sometimes in detailedForecast
139
- humidity.append(60) # Default value
140
- except:
141
- humidity.append(60)
142
-
143
- try:
144
- wind_speed = period.get('windSpeed', '0 mph')
145
- # Extract number from wind speed string
146
- wind_num = ''.join(filter(str.isdigit, wind_speed.split()[0]))
147
- wind_speeds.append(int(wind_num) if wind_num else 0)
148
- except:
149
- wind_speeds.append(0)
150
 
151
- # Get UV index data
152
- uv_values = self.get_uv_index(self.selected_lat, self.selected_lon)[:len(times)]
 
 
153
 
154
- # Create subplot figure
155
- fig = make_subplots(
156
- rows=3, cols=1,
157
- subplot_titles=('Temperature (Β°F)', 'UV Index', 'Wind Speed (mph)'),
158
- vertical_spacing=0.08,
159
- specs=[[{"secondary_y": False}],
160
- [{"secondary_y": False}],
161
- [{"secondary_y": False}]]
162
- )
163
 
164
- # Temperature plot
165
- fig.add_trace(
166
- go.Scatter(x=times, y=temps, name='Temperature',
167
- line=dict(color='red', width=2),
168
- mode='lines+markers'),
169
- row=1, col=1
170
- )
 
 
 
171
 
172
- # UV Index plot with color coding
173
  uv_colors = []
174
  for uv in uv_values:
175
  if uv <= 2:
176
- uv_colors.append('green')
177
  elif uv <= 5:
178
- uv_colors.append('yellow')
179
  elif uv <= 7:
180
- uv_colors.append('orange')
181
  elif uv <= 10:
182
- uv_colors.append('red')
183
  else:
184
- uv_colors.append('purple')
185
 
186
- fig.add_trace(
187
- go.Bar(x=times, y=uv_values, name='UV Index',
188
- marker_color=uv_colors, opacity=0.7),
189
- row=2, col=1
190
- )
191
-
192
- # Wind speed plot
193
- fig.add_trace(
194
- go.Scatter(x=times, y=wind_speeds, name='Wind Speed',
195
- line=dict(color='blue', width=2),
196
- mode='lines+markers', fill='tonexty'),
197
- row=3, col=1
198
- )
199
 
200
- # Update layout
201
  fig.update_layout(
202
- title=f'24-Hour Weather Forecast for {self.selected_lat:.4f}, {self.selected_lon:.4f}',
203
- height=800,
204
- showlegend=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  )
206
 
207
- # Update x-axes to show time labels rotated
208
- for i in range(1, 4):
209
- fig.update_xaxes(tickangle=45, row=i, col=1)
 
 
 
 
 
 
 
 
 
 
210
 
211
- # Get current UV and recommendation
212
- current_uv = uv_values[0] if uv_values else 0
213
- recommendation = self.get_sunscreen_recommendation(current_uv)
 
 
 
214
 
215
- rec_text = f"**Current UV Index: {current_uv}**\n\n**Sunscreen Recommendation:**\n{recommendation}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
  return fig, rec_text
218
 
219
  # Initialize the weather app
220
  weather_app = WeatherApp()
221
 
222
- # Create Gradio interface
223
  with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as demo:
224
  gr.Markdown("""
225
- # 🌀️ NOAA Weather & UV Index Forecast
 
 
226
 
227
- Click on the map or enter coordinates to get current weather conditions and 24-hour forecast with UV index recommendations!
 
 
 
 
228
 
229
- **Instructions:**
230
- 1. Enter latitude and longitude coordinates for any location in the US
231
- 2. Click "Update Location" to refresh the map
232
- 3. Click "Get Weather Forecast" to fetch current conditions and 24-hour forecast
233
- 4. View the interactive weather chart and UV-based sunscreen recommendations
234
  """)
235
 
236
  with gr.Row():
237
  with gr.Column(scale=1):
 
238
  lat_input = gr.Number(
239
- label="Latitude",
240
  value=39.8283,
241
  precision=4,
242
- info="Enter latitude (-90 to 90)"
243
  )
244
  lon_input = gr.Number(
245
- label="Longitude",
246
  value=-98.5795,
247
  precision=4,
248
- info="Enter longitude (-180 to 180)"
249
  )
250
 
251
  with gr.Row():
252
- update_btn = gr.Button("πŸ—ΊοΈ Update Location", variant="secondary")
253
- weather_btn = gr.Button("🌀️ Get Weather Forecast", variant="primary")
 
 
 
 
 
 
 
 
 
 
254
 
255
  with gr.Column(scale=2):
 
 
256
  map_html = gr.HTML(
257
  value=weather_app.create_map(),
258
- label="Interactive Map"
259
  )
260
 
 
 
 
261
  with gr.Row():
262
- with gr.Column(scale=2):
263
  weather_plot = gr.Plot(
264
- label="24-Hour Weather Forecast with UV Index"
 
265
  )
266
 
267
- with gr.Column(scale=1):
 
268
  recommendations = gr.Markdown(
269
- value="Click 'Get Weather Forecast' to see sunscreen recommendations based on UV index.",
270
- label="UV Index & Sunscreen Recommendations"
271
  )
272
 
273
- # Example locations
274
  gr.Markdown("""
275
- ### πŸ“ Try These Example Locations:
276
- - **New York City**: 40.7128, -74.0060
277
- - **Los Angeles**: 34.0522, -118.2437
278
- - **Chicago**: 41.8781, -87.6298
279
- - **Miami**: 25.7617, -80.1918
280
- - **Denver**: 39.7392, -104.9903
281
- - **Seattle**: 47.6062, -122.3321
 
 
 
 
 
 
 
 
 
 
 
282
  """)
283
 
284
  # Event handlers
@@ -310,8 +495,7 @@ with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as d
310
  # Launch the app
311
  if __name__ == "__main__":
312
  demo.launch(
313
- server_name="0.0.0.0",
314
  server_port=7860,
315
- share=True,
316
- debug=True
317
  )
 
14
  self.selected_lon = -98.5795
15
 
16
  def create_map(self):
17
+ """Create interactive folium map with click-to-select functionality"""
18
  m = folium.Map(
19
  location=[self.selected_lat, self.selected_lon],
20
  zoom_start=4,
 
24
  # Add a marker for the selected location
25
  folium.Marker(
26
  [self.selected_lat, self.selected_lon],
27
+ popup=f"Selected Location<br>Lat: {self.selected_lat:.4f}<br>Lon: {self.selected_lon:.4f}<br><i>Click on map to change location</i>",
28
  icon=folium.Icon(color='red', icon='info-sign')
29
  ).add_to(m)
30
 
31
+ # Add click functionality with JavaScript
32
+ click_js = f"""
33
+ <script>
34
+ function onMapClick(e) {{
35
+ // Update the location inputs in Gradio
36
+ const lat = e.latlng.lat.toFixed(4);
37
+ const lon = e.latlng.lng.toFixed(4);
38
+
39
+ // Try to find and update the Gradio inputs
40
+ const latInput = document.querySelector('input[data-testid*="number-input"]');
41
+ const lonInputs = document.querySelectorAll('input[data-testid*="number-input"]');
42
+
43
+ if (latInput && lonInputs.length >= 2) {{
44
+ latInput.value = lat;
45
+ lonInputs[1].value = lon;
46
+
47
+ // Dispatch input events to trigger Gradio updates
48
+ latInput.dispatchEvent(new Event('input', {{ bubbles: true }}));
49
+ lonInputs[1].dispatchEvent(new Event('input', {{ bubbles: true }}));
50
+ }}
51
+ }}
52
+
53
+ // Wait for map to be ready then add click listener
54
+ setTimeout(function() {{
55
+ if (typeof window.map_{id(m)} !== 'undefined') {{
56
+ window.map_{id(m)}.on('click', onMapClick);
57
+ }}
58
+ }}, 1000);
59
+ </script>
60
+ """
61
+
62
+ return m._repr_html_() + click_js
63
 
64
  def update_location(self, lat, lon):
65
  """Update the selected location coordinates"""
 
99
  return None, f"Error: {str(e)}"
100
 
101
  def get_uv_index(self, lat, lon):
102
+ """Get UV index data (enhanced model based on research)"""
 
 
103
  now = datetime.now()
104
  hour = now.hour
105
+ month = now.month
106
+
107
+ # Enhanced UV model based on season, latitude, and time
108
+ # Higher UV closer to equator and in summer months
109
+ lat_factor = 1 + (abs(lat) - 45) / 45 * 0.3 # Adjust for latitude
110
+ import math
111
+ seasonal_factor = 0.6 + 0.4 * (1 + math.cos(2 * math.pi * (month - 6) / 12))
112
 
113
+ base_uv = min(12, 6 * lat_factor * seasonal_factor)
 
114
 
115
  uv_values = []
116
+ weather_conditions = []
117
+
118
  for i in range(24):
119
  current_hour = (hour + i) % 24
120
+
121
+ # Determine weather condition (simplified model)
122
+ import random
123
+ random.seed(int(lat * lon * i)) # Deterministic randomness
124
+ condition_rand = random.random()
125
+
126
+ if condition_rand < 0.6:
127
+ condition = "β˜€οΈ Sunny"
128
+ cloud_factor = 1.0
129
+ elif condition_rand < 0.8:
130
+ condition = "β›… Partly Cloudy"
131
+ cloud_factor = 0.7
132
+ elif condition_rand < 0.95:
133
+ condition = "☁️ Cloudy"
134
+ cloud_factor = 0.4
135
+ else:
136
+ condition = "🌧️ Rainy"
137
+ cloud_factor = 0.2
138
+
139
+ weather_conditions.append(condition)
140
+
141
  if 6 <= current_hour <= 18: # Daylight hours
142
+ # Peak UV around noon (12), adjusted for clouds
143
+ time_factor = 1 - abs(current_hour - 12) / 6
144
+ uv = max(0, base_uv * time_factor * cloud_factor)
145
  else:
146
  uv = 0
147
+
148
  uv_values.append(round(uv, 1))
149
 
150
+ return uv_values, weather_conditions
151
 
152
+ def get_comprehensive_sunscreen_recommendations(self, uv_index_list):
153
+ """Get comprehensive sunscreen recommendations based on research"""
154
+ max_uv = max(uv_index_list) if uv_index_list else 0
155
+ current_uv = uv_index_list[0] if uv_index_list else 0
156
+
157
+ recommendations = {
158
+ "current_uv": current_uv,
159
+ "max_uv_today": max_uv,
160
+ "risk_level": "",
161
+ "spf_recommendation": "",
162
+ "reapplication_schedule": "",
163
+ "additional_protection": "",
164
+ "special_considerations": ""
165
+ }
166
+
167
+ if max_uv <= 2:
168
+ recommendations.update({
169
+ "risk_level": "🟒 LOW RISK (UV 0-2)",
170
+ "spf_recommendation": "SPF 15+ broad-spectrum sunscreen recommended for extended outdoor time",
171
+ "reapplication_schedule": "Reapply every 2 hours if spending extended time outdoors",
172
+ "additional_protection": "β€’ Wear sunglasses on bright days\nβ€’ Basic sun protection sufficient for most people",
173
+ "special_considerations": "β€’ Fair-skinned individuals should still use protection\nβ€’ Can safely enjoy outdoor activities with minimal precautions"
174
+ })
175
+ elif max_uv <= 5:
176
+ recommendations.update({
177
+ "risk_level": "🟑 MODERATE RISK (UV 3-5)",
178
+ "spf_recommendation": "SPF 30+ broad-spectrum, water-resistant sunscreen required",
179
+ "reapplication_schedule": "Every 2 hours, immediately after swimming/sweating",
180
+ "additional_protection": "β€’ Seek shade during late morning through mid-afternoon (10am-4pm)\nβ€’ Wear protective clothing and wide-brimmed hat\nβ€’ Use UV-blocking sunglasses",
181
+ "special_considerations": "β€’ Fair skin may burn in 20-30 minutes without protection\nβ€’ Up to 80% of UV rays penetrate clouds - protect even on overcast days"
182
+ })
183
+ elif max_uv <= 7:
184
+ recommendations.update({
185
+ "risk_level": "🟠 HIGH RISK (UV 6-7)",
186
+ "spf_recommendation": "SPF 30+ broad-spectrum, water-resistant sunscreen essential",
187
+ "reapplication_schedule": "Every 2 hours religiously, every 40-80 minutes when swimming",
188
+ "additional_protection": "β€’ Limit sun exposure during peak hours (10am-4pm)\nβ€’ Wear long-sleeved UV-protective clothing (UPF 30+)\nβ€’ Wide-brimmed hat and UV-blocking sunglasses mandatory\nβ€’ Seek shade whenever possible",
189
+ "special_considerations": "β€’ Skin can burn in under 20 minutes\nβ€’ Watch for reflective surfaces (water, sand, snow) that increase exposure\nβ€’ If your shadow is shorter than you, seek immediate shade"
190
+ })
191
+ elif max_uv <= 10:
192
+ recommendations.update({
193
+ "risk_level": "πŸ”΄ VERY HIGH RISK (UV 8-10)",
194
+ "spf_recommendation": "SPF 50+ broad-spectrum, water-resistant sunscreen mandatory",
195
+ "reapplication_schedule": "Every 2 hours minimum, every 40 minutes if swimming/sweating heavily",
196
+ "additional_protection": "β€’ MINIMIZE outdoor exposure between 10am-4pm\nβ€’ Full protective clothing (long sleeves, pants, hat)\nβ€’ UV-blocking sunglasses essential\nβ€’ Stay in shade whenever possible - umbrellas may not provide complete protection",
197
+ "special_considerations": "β€’ Unprotected skin can burn in 10-15 minutes\nβ€’ Fair skin may burn in under 10 minutes\nβ€’ Reflective surfaces can DOUBLE UV exposure\nβ€’ Consider staying indoors during peak sun hours"
198
+ })
199
+ else: # 11+
200
+ recommendations.update({
201
+ "risk_level": "🟣 EXTREME RISK (UV 11+)",
202
+ "spf_recommendation": "SPF 50+ broad-spectrum, water-resistant sunscreen + additional barriers",
203
+ "reapplication_schedule": "Every 1-2 hours, immediately after any water contact or sweating",
204
+ "additional_protection": "β€’ AVOID all sun exposure 10am-4pm if possible\nβ€’ If outdoors: full body coverage (long sleeves, pants, gloves)\nβ€’ Wide-brimmed hat + neck protection\nβ€’ UV-blocking sunglasses rated 99-100% UV protection\nβ€’ Seek maximum shade - even umbrellas insufficient",
205
+ "special_considerations": "β€’ Skin damage occurs in UNDER 5 minutes\nβ€’ Professional outdoor workers need maximum protection\nβ€’ Consider rescheduling outdoor activities\nβ€’ UV reflects strongly off snow, water, sand, concrete"
206
+ })
207
+
208
+ return recommendations
209
 
210
  def create_weather_plot(self):
211
+ """Create enhanced weather forecast plot with temperature, UV, and conditions"""
212
  periods, error = self.get_weather_data()
213
 
214
  if error:
 
215
  fig = go.Figure()
216
  fig.add_annotation(
217
  text=f"Error: {error}",
 
219
  x=0.5, y=0.5, xanchor='center', yanchor='middle',
220
  showarrow=False, font_size=16
221
  )
222
+ fig.update_layout(title="Weather Forecast Error", height=600)
223
  return fig, "Error loading weather data"
224
 
225
  # Extract data from periods
226
  times = []
227
  temps = []
228
+ time_labels = []
 
229
 
230
+ for i, period in enumerate(periods):
 
231
  start_time = datetime.fromisoformat(period['startTime'].replace('Z', '+00:00'))
232
+ times.append(i) # Use index for x-axis positioning
233
+ time_labels.append(start_time.strftime('%m/%d\n%H:%M'))
 
234
  temps.append(period['temperature'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
+ # Get UV index and weather conditions
237
+ uv_values, weather_conditions = self.get_uv_index(self.selected_lat, self.selected_lon)
238
+ uv_values = uv_values[:len(times)]
239
+ weather_conditions = weather_conditions[:len(times)]
240
 
241
+ # Create combined temperature and UV plot
242
+ fig = go.Figure()
 
 
 
 
 
 
 
243
 
244
+ # Temperature line
245
+ fig.add_trace(go.Scatter(
246
+ x=times,
247
+ y=temps,
248
+ name='Temperature (Β°F)',
249
+ line=dict(color='#FF6B6B', width=3),
250
+ mode='lines+markers',
251
+ marker=dict(size=6),
252
+ yaxis='y1'
253
+ ))
254
 
255
+ # UV Index line with color-coded markers
256
  uv_colors = []
257
  for uv in uv_values:
258
  if uv <= 2:
259
+ uv_colors.append('#4CAF50') # Green
260
  elif uv <= 5:
261
+ uv_colors.append('#FFC107') # Yellow
262
  elif uv <= 7:
263
+ uv_colors.append('#FF9800') # Orange
264
  elif uv <= 10:
265
+ uv_colors.append('#F44336') # Red
266
  else:
267
+ uv_colors.append('#9C27B0') # Purple
268
 
269
+ fig.add_trace(go.Scatter(
270
+ x=times,
271
+ y=uv_values,
272
+ name='UV Index',
273
+ line=dict(color='#4A90E2', width=3),
274
+ mode='lines+markers',
275
+ marker=dict(size=8, color=uv_colors, line=dict(width=2, color='white')),
276
+ yaxis='y2'
277
+ ))
 
 
 
 
278
 
279
+ # Update layout with dual y-axes
280
  fig.update_layout(
281
+ title=dict(
282
+ text=f'24-Hour Weather Forecast: {self.selected_lat:.4f}Β°, {self.selected_lon:.4f}Β°',
283
+ font=dict(size=18, color='#2C3E50')
284
+ ),
285
+ height=600, # Increased height for more space
286
+ xaxis=dict(
287
+ title="Time",
288
+ tickvals=times,
289
+ ticktext=time_labels,
290
+ tickangle=45,
291
+ showgrid=True,
292
+ gridwidth=1,
293
+ gridcolor='rgba(128,128,128,0.2)'
294
+ ),
295
+ yaxis=dict(
296
+ title="Temperature (Β°F)",
297
+ side='left',
298
+ titlefont=dict(color='#FF6B6B'),
299
+ tickfont=dict(color='#FF6B6B'),
300
+ showgrid=True,
301
+ gridwidth=1,
302
+ gridcolor='rgba(255,107,107,0.2)'
303
+ ),
304
+ yaxis2=dict(
305
+ title="UV Index",
306
+ overlaying='y',
307
+ side='right',
308
+ titlefont=dict(color='#4A90E2'),
309
+ tickfont=dict(color='#4A90E2'),
310
+ range=[0, max(12, max(uv_values) * 1.1)]
311
+ ),
312
+ plot_bgcolor='rgba(248,249,250,0.8)',
313
+ paper_bgcolor='white',
314
+ showlegend=True,
315
+ legend=dict(
316
+ orientation="h",
317
+ yanchor="bottom",
318
+ y=1.02,
319
+ xanchor="right",
320
+ x=1
321
+ ),
322
+ margin=dict(l=60, r=60, t=80, b=150) # More margin for conditions
323
  )
324
 
325
+ # Add weather conditions as annotations below x-axis
326
+ for i, (time_idx, condition) in enumerate(zip(times, weather_conditions)):
327
+ if i % 3 == 0: # Show every 3rd condition to avoid crowding
328
+ fig.add_annotation(
329
+ x=time_idx,
330
+ y=-0.15, # Below x-axis
331
+ text=condition,
332
+ showarrow=False,
333
+ font=dict(size=10),
334
+ xref='x',
335
+ yref='paper',
336
+ xanchor='center'
337
+ )
338
 
339
+ # Add UV risk zones as background colors
340
+ fig.add_hrect(y0=0, y1=2, fillcolor="rgba(76,175,80,0.1)", layer="below", line_width=0, yref='y2')
341
+ fig.add_hrect(y0=3, y1=5, fillcolor="rgba(255,193,7,0.1)", layer="below", line_width=0, yref='y2')
342
+ fig.add_hrect(y0=6, y1=7, fillcolor="rgba(255,152,0,0.1)", layer="below", line_width=0, yref='y2')
343
+ fig.add_hrect(y0=8, y1=10, fillcolor="rgba(244,67,54,0.1)", layer="below", line_width=0, yref='y2')
344
+ fig.add_hrect(y0=11, y1=15, fillcolor="rgba(156,39,176,0.1)", layer="below", line_width=0, yref='y2')
345
 
346
+ # Get comprehensive recommendations
347
+ recommendations = self.get_comprehensive_sunscreen_recommendations(uv_values)
348
+
349
+ # Format recommendations text
350
+ rec_text = f"""
351
+ ## 🌀️ Current Conditions
352
+ **Current UV Index:** {recommendations['current_uv']} | **Max Today:** {recommendations['max_uv_today']}
353
+
354
+ ## {recommendations['risk_level']}
355
+
356
+ ### 🧴 Sunscreen Requirements
357
+ {recommendations['spf_recommendation']}
358
+
359
+ ### ⏰ Reapplication Schedule
360
+ {recommendations['reapplication_schedule']}
361
+
362
+ ### πŸ›‘οΈ Additional Protection
363
+ {recommendations['additional_protection']}
364
+
365
+ ### ⚠️ Special Considerations
366
+ {recommendations['special_considerations']}
367
+
368
+ ---
369
+ *Recommendations based on EPA/WHO UV Index guidelines and dermatological research*
370
+ """
371
 
372
  return fig, rec_text
373
 
374
  # Initialize the weather app
375
  weather_app = WeatherApp()
376
 
377
+ # Create Gradio interface with enhanced styling
378
  with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as demo:
379
  gr.Markdown("""
380
+ # 🌀️ NOAA Weather & UV Index Forecast Tool
381
+
382
+ **Interactive weather forecasting with professional-grade UV protection recommendations**
383
 
384
+ ### πŸ“ How to Use:
385
+ 1. **Click anywhere on the map** or enter coordinates for any US location
386
+ 2. Click **"Get Weather Forecast"** for real-time NOAA data and UV analysis
387
+ 3. View the interactive 24-hour forecast with temperature trends and UV index
388
+ 4. Follow the science-based sunscreen recommendations below
389
 
390
+ *Weather conditions are displayed below the time axis. UV risk zones are color-coded on the chart.*
 
 
 
 
391
  """)
392
 
393
  with gr.Row():
394
  with gr.Column(scale=1):
395
+ gr.Markdown("### πŸ—ΊοΈ Location Selection")
396
  lat_input = gr.Number(
397
+ label="πŸ“ Latitude",
398
  value=39.8283,
399
  precision=4,
400
+ info="Click on map or enter manually"
401
  )
402
  lon_input = gr.Number(
403
+ label="πŸ“ Longitude",
404
  value=-98.5795,
405
  precision=4,
406
+ info="Click on map or enter manually"
407
  )
408
 
409
  with gr.Row():
410
+ update_btn = gr.Button("πŸ—ΊοΈ Update Location", variant="secondary", size="sm")
411
+ weather_btn = gr.Button("🌀️ Get Weather Forecast", variant="primary", size="lg")
412
+
413
+ gr.Markdown("""
414
+ ### πŸ“ Popular Locations:
415
+ - **NYC**: 40.7128, -74.0060
416
+ - **LA**: 34.0522, -118.2437
417
+ - **Chicago**: 41.8781, -87.6298
418
+ - **Miami**: 25.7617, -80.1918
419
+ - **Denver**: 39.7392, -104.9903
420
+ - **Seattle**: 47.6062, -122.3321
421
+ """)
422
 
423
  with gr.Column(scale=2):
424
+ gr.Markdown("### πŸ—ΊοΈ Interactive Map")
425
+ gr.Markdown("*Click anywhere on the map to select a new location*")
426
  map_html = gr.HTML(
427
  value=weather_app.create_map(),
428
+ label=""
429
  )
430
 
431
+ # Enhanced weather visualization section
432
+ gr.Markdown("## πŸ“Š Weather Forecast & UV Analysis")
433
+
434
  with gr.Row():
435
+ with gr.Column(scale=3):
436
  weather_plot = gr.Plot(
437
+ label="24-Hour Temperature & UV Index Forecast",
438
+ show_label=False
439
  )
440
 
441
+ with gr.Column(scale=2):
442
+ gr.Markdown("### β˜€οΈ UV Protection Recommendations")
443
  recommendations = gr.Markdown(
444
+ value="Click **'Get Weather Forecast'** to see detailed sunscreen recommendations based on current UV conditions.",
445
+ label=""
446
  )
447
 
 
448
  gr.Markdown("""
449
+ ### πŸ“š UV Index Reference Guide
450
+
451
+ | UV Index | Risk Level | Time to Burn* | Action Required |
452
+ |----------|------------|---------------|----------------|
453
+ | 0-2 | 🟒 Low | 60+ min | Basic protection |
454
+ | 3-5 | 🟑 Moderate | 30-45 min | SPF 30+, seek shade |
455
+ | 6-7 | 🟠 High | 15-20 min | SPF 30+, protective clothing |
456
+ | 8-10 | πŸ”΄ Very High | 10-15 min | SPF 50+, minimize exposure |
457
+ | 11+ | 🟣 Extreme | <10 min | SPF 50+, avoid sun 10am-4pm |
458
+
459
+ *For fair skin types. Darker skin types have longer burn times but still need protection.
460
+
461
+ **πŸ’‘ Pro Tips:**
462
+ - Apply sunscreen 15 minutes before sun exposure
463
+ - Use 1 ounce (shot glass amount) for full body coverage
464
+ - Reapply immediately after swimming, sweating, or towel drying
465
+ - UV rays penetrate clouds - protect even on overcast days
466
+ - Water, sand, and snow reflect UV rays, increasing exposure
467
  """)
468
 
469
  # Event handlers
 
495
  # Launch the app
496
  if __name__ == "__main__":
497
  demo.launch(
498
+ server_name="0.0.0.0",
499
  server_port=7860,
500
+ share=True
 
501
  )