Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -35,9 +35,28 @@ class WeatherApp:
|
|
35 |
try:
|
36 |
self.selected_lat = float(lat)
|
37 |
self.selected_lon = float(lon)
|
38 |
-
return self.create_map()
|
39 |
except:
|
40 |
-
return self.create_map()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
def get_weather_data(self):
|
43 |
"""Fetch weather data from NOAA API"""
|
@@ -87,22 +106,23 @@ class WeatherApp:
|
|
87 |
for i in range(24):
|
88 |
current_hour = (hour + i) % 24
|
89 |
|
90 |
-
# Determine weather condition (
|
91 |
import random
|
92 |
-
random.seed(int(lat * lon * i)) # Deterministic randomness
|
93 |
condition_rand = random.random()
|
94 |
|
95 |
-
|
96 |
-
|
|
|
97 |
cloud_factor = 1.0
|
98 |
-
elif condition_rand < 0.
|
99 |
-
condition = "
|
100 |
cloud_factor = 0.7
|
101 |
-
elif condition_rand < 0.
|
102 |
-
condition = "
|
103 |
cloud_factor = 0.4
|
104 |
else:
|
105 |
-
condition = "
|
106 |
cloud_factor = 0.2
|
107 |
|
108 |
weather_conditions.append(condition)
|
@@ -202,10 +222,10 @@ class WeatherApp:
|
|
202 |
time_labels.append(start_time.strftime('%m/%d\n%H:%M'))
|
203 |
temps.append(period['temperature'])
|
204 |
|
205 |
-
# Get UV index and weather conditions
|
206 |
uv_values, weather_conditions = self.get_uv_index(self.selected_lat, self.selected_lon)
|
207 |
-
uv_values = uv_values[:len(times)]
|
208 |
-
weather_conditions = weather_conditions[:len(times)]
|
209 |
|
210 |
# Create combined temperature and UV plot
|
211 |
fig = go.Figure()
|
@@ -245,13 +265,13 @@ class WeatherApp:
|
|
245 |
yaxis='y2'
|
246 |
))
|
247 |
|
248 |
-
# Update layout with dual y-axes
|
249 |
fig.update_layout(
|
250 |
title=dict(
|
251 |
text=f'24-Hour Weather Forecast: {self.selected_lat:.4f}°, {self.selected_lon:.4f}°',
|
252 |
font=dict(size=18, color='#2C3E50')
|
253 |
),
|
254 |
-
height=
|
255 |
xaxis=dict(
|
256 |
title="Time",
|
257 |
tickvals=times,
|
@@ -259,7 +279,9 @@ class WeatherApp:
|
|
259 |
tickangle=45,
|
260 |
showgrid=True,
|
261 |
gridwidth=1,
|
262 |
-
gridcolor='rgba(128,128,128,0.2)'
|
|
|
|
|
263 |
),
|
264 |
yaxis=dict(
|
265 |
title=dict(text="Temperature (°F)", font=dict(color='#FF6B6B')),
|
@@ -267,14 +289,16 @@ class WeatherApp:
|
|
267 |
tickfont=dict(color='#FF6B6B'),
|
268 |
showgrid=True,
|
269 |
gridwidth=1,
|
270 |
-
gridcolor='rgba(255,107,107,0.2)'
|
|
|
271 |
),
|
272 |
yaxis2=dict(
|
273 |
title=dict(text="UV Index", font=dict(color='#4A90E2')),
|
274 |
overlaying='y',
|
275 |
side='right',
|
276 |
tickfont=dict(color='#4A90E2'),
|
277 |
-
range=[0, max(12, max(uv_values) * 1.1)]
|
|
|
278 |
),
|
279 |
plot_bgcolor='rgba(248,249,250,0.8)',
|
280 |
paper_bgcolor='white',
|
@@ -286,29 +310,34 @@ class WeatherApp:
|
|
286 |
xanchor="right",
|
287 |
x=1
|
288 |
),
|
289 |
-
margin=dict(l=
|
|
|
290 |
)
|
291 |
|
292 |
-
#
|
|
|
|
|
|
|
293 |
for i, (time_idx, condition) in enumerate(zip(times, weather_conditions)):
|
294 |
-
if i %
|
295 |
fig.add_annotation(
|
296 |
x=time_idx,
|
297 |
-
y=-0.
|
298 |
-
text=condition,
|
299 |
showarrow=False,
|
300 |
-
font=dict(size=
|
301 |
xref='x',
|
302 |
yref='paper',
|
303 |
xanchor='center'
|
304 |
)
|
305 |
|
306 |
# Add UV risk zones as background colors
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
|
|
312 |
|
313 |
# Get comprehensive recommendations
|
314 |
recommendations = self.get_comprehensive_sunscreen_recommendations(uv_values)
|
@@ -377,14 +406,26 @@ with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as d
|
|
377 |
update_btn = gr.Button("🗺️ Update Location", variant="secondary", size="sm")
|
378 |
weather_btn = gr.Button("🧴 Get Sunscreen Report", variant="primary", size="lg")
|
379 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
380 |
gr.Markdown("""
|
381 |
-
### 📍
|
382 |
- **NYC**: 40.7128, -74.0060
|
383 |
- **LA**: 34.0522, -118.2437
|
384 |
- **Chicago**: 41.8781, -87.6298
|
385 |
- **Miami**: 25.7617, -80.1918
|
386 |
- **Denver**: 39.7392, -104.9903
|
387 |
- **Seattle**: 47.6062, -122.3321
|
|
|
388 |
""")
|
389 |
|
390 |
with gr.Column(scale=2):
|
@@ -436,7 +477,37 @@ with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as d
|
|
436 |
update_btn.click(
|
437 |
fn=weather_app.update_location,
|
438 |
inputs=[lat_input, lon_input],
|
439 |
-
outputs=[map_html]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
440 |
)
|
441 |
|
442 |
weather_btn.click(
|
@@ -449,13 +520,13 @@ with gr.Blocks(title="NOAA Weather & UV Index Map", theme=gr.themes.Soft()) as d
|
|
449 |
lat_input.change(
|
450 |
fn=weather_app.update_location,
|
451 |
inputs=[lat_input, lon_input],
|
452 |
-
outputs=[map_html]
|
453 |
)
|
454 |
|
455 |
lon_input.change(
|
456 |
fn=weather_app.update_location,
|
457 |
inputs=[lat_input, lon_input],
|
458 |
-
outputs=[map_html]
|
459 |
)
|
460 |
|
461 |
# Launch the app
|
|
|
35 |
try:
|
36 |
self.selected_lat = float(lat)
|
37 |
self.selected_lon = float(lon)
|
38 |
+
return self.create_map(), lat, lon
|
39 |
except:
|
40 |
+
return self.create_map(), self.selected_lat, self.selected_lon
|
41 |
+
|
42 |
+
def set_city_coordinates(self, city_name):
|
43 |
+
"""Set coordinates for major cities"""
|
44 |
+
cities = {
|
45 |
+
"New York City": (40.7128, -74.0060),
|
46 |
+
"Los Angeles": (34.0522, -118.2437),
|
47 |
+
"Chicago": (41.8781, -87.6298),
|
48 |
+
"Miami": (25.7617, -80.1918),
|
49 |
+
"Denver": (39.7392, -104.9903),
|
50 |
+
"Seattle": (47.6062, -122.3321),
|
51 |
+
"Bozeman, MT": (45.6770, -111.0429)
|
52 |
+
}
|
53 |
+
|
54 |
+
if city_name in cities:
|
55 |
+
lat, lon = cities[city_name]
|
56 |
+
self.selected_lat = lat
|
57 |
+
self.selected_lon = lon
|
58 |
+
return self.create_map(), lat, lon
|
59 |
+
return self.create_map(), self.selected_lat, self.selected_lon
|
60 |
|
61 |
def get_weather_data(self):
|
62 |
"""Fetch weather data from NOAA API"""
|
|
|
106 |
for i in range(24):
|
107 |
current_hour = (hour + i) % 24
|
108 |
|
109 |
+
# Determine weather condition (more realistic distribution - less sunny)
|
110 |
import random
|
111 |
+
random.seed(int(lat * lon * i + 42)) # Deterministic randomness
|
112 |
condition_rand = random.random()
|
113 |
|
114 |
+
# Reduced sunny probability to make it more realistic
|
115 |
+
if condition_rand < 0.35: # Reduced from 0.6 to 0.35
|
116 |
+
condition = "Sunny"
|
117 |
cloud_factor = 1.0
|
118 |
+
elif condition_rand < 0.60: # Increased partly cloudy
|
119 |
+
condition = "Partly Cloudy"
|
120 |
cloud_factor = 0.7
|
121 |
+
elif condition_rand < 0.85: # Increased cloudy
|
122 |
+
condition = "Cloudy"
|
123 |
cloud_factor = 0.4
|
124 |
else:
|
125 |
+
condition = "Rainy"
|
126 |
cloud_factor = 0.2
|
127 |
|
128 |
weather_conditions.append(condition)
|
|
|
222 |
time_labels.append(start_time.strftime('%m/%d\n%H:%M'))
|
223 |
temps.append(period['temperature'])
|
224 |
|
225 |
+
# Get UV index and weather conditions - USE SAME LENGTH AS TEMPERATURE DATA
|
226 |
uv_values, weather_conditions = self.get_uv_index(self.selected_lat, self.selected_lon)
|
227 |
+
uv_values = uv_values[:len(times)] # Ensure same length
|
228 |
+
weather_conditions = weather_conditions[:len(times)] # Ensure same length
|
229 |
|
230 |
# Create combined temperature and UV plot
|
231 |
fig = go.Figure()
|
|
|
265 |
yaxis='y2'
|
266 |
))
|
267 |
|
268 |
+
# Update layout with dual y-axes and better spacing
|
269 |
fig.update_layout(
|
270 |
title=dict(
|
271 |
text=f'24-Hour Weather Forecast: {self.selected_lat:.4f}°, {self.selected_lon:.4f}°',
|
272 |
font=dict(size=18, color='#2C3E50')
|
273 |
),
|
274 |
+
height=700, # Increased height for more space
|
275 |
xaxis=dict(
|
276 |
title="Time",
|
277 |
tickvals=times,
|
|
|
279 |
tickangle=45,
|
280 |
showgrid=True,
|
281 |
gridwidth=1,
|
282 |
+
gridcolor='rgba(128,128,128,0.2)',
|
283 |
+
range=[-1.5, len(times) + 0.5], # More padding on sides to prevent squishing
|
284 |
+
fixedrange=True # Disable zooming/panning
|
285 |
),
|
286 |
yaxis=dict(
|
287 |
title=dict(text="Temperature (°F)", font=dict(color='#FF6B6B')),
|
|
|
289 |
tickfont=dict(color='#FF6B6B'),
|
290 |
showgrid=True,
|
291 |
gridwidth=1,
|
292 |
+
gridcolor='rgba(255,107,107,0.2)',
|
293 |
+
fixedrange=True # Disable zooming/panning
|
294 |
),
|
295 |
yaxis2=dict(
|
296 |
title=dict(text="UV Index", font=dict(color='#4A90E2')),
|
297 |
overlaying='y',
|
298 |
side='right',
|
299 |
tickfont=dict(color='#4A90E2'),
|
300 |
+
range=[0, max(12, max(uv_values) * 1.1) if uv_values else 12],
|
301 |
+
fixedrange=True # Disable zooming/panning
|
302 |
),
|
303 |
plot_bgcolor='rgba(248,249,250,0.8)',
|
304 |
paper_bgcolor='white',
|
|
|
310 |
xanchor="right",
|
311 |
x=1
|
312 |
),
|
313 |
+
margin=dict(l=100, r=100, t=100, b=220), # Much larger margins, especially on sides
|
314 |
+
dragmode=False, # Disable all dragging
|
315 |
)
|
316 |
|
317 |
+
# Disable hover interactions
|
318 |
+
fig.update_traces(hoverinfo='none')
|
319 |
+
|
320 |
+
# Add weather conditions as text annotations with better spacing and readability
|
321 |
for i, (time_idx, condition) in enumerate(zip(times, weather_conditions)):
|
322 |
+
if i % 2 == 0: # Show every other condition to avoid crowding
|
323 |
fig.add_annotation(
|
324 |
x=time_idx,
|
325 |
+
y=-0.28, # Further below x-axis
|
326 |
+
text=f"<b>{condition}</b>",
|
327 |
showarrow=False,
|
328 |
+
font=dict(size=12, color='#2C3E50'),
|
329 |
xref='x',
|
330 |
yref='paper',
|
331 |
xanchor='center'
|
332 |
)
|
333 |
|
334 |
# Add UV risk zones as background colors
|
335 |
+
if uv_values:
|
336 |
+
fig.add_hrect(y0=0, y1=2, fillcolor="rgba(76,175,80,0.1)", layer="below", line_width=0, yref='y2')
|
337 |
+
fig.add_hrect(y0=3, y1=5, fillcolor="rgba(255,193,7,0.1)", layer="below", line_width=0, yref='y2')
|
338 |
+
fig.add_hrect(y0=6, y1=7, fillcolor="rgba(255,152,0,0.1)", layer="below", line_width=0, yref='y2')
|
339 |
+
fig.add_hrect(y0=8, y1=10, fillcolor="rgba(244,67,54,0.1)", layer="below", line_width=0, yref='y2')
|
340 |
+
fig.add_hrect(y0=11, y1=15, fillcolor="rgba(156,39,176,0.1)", layer="below", line_width=0, yref='y2')
|
341 |
|
342 |
# Get comprehensive recommendations
|
343 |
recommendations = self.get_comprehensive_sunscreen_recommendations(uv_values)
|
|
|
406 |
update_btn = gr.Button("🗺️ Update Location", variant="secondary", size="sm")
|
407 |
weather_btn = gr.Button("🧴 Get Sunscreen Report", variant="primary", size="lg")
|
408 |
|
409 |
+
gr.Markdown("### 🏙️ Quick City Selection")
|
410 |
+
with gr.Row():
|
411 |
+
nyc_btn = gr.Button("🗽 NYC", size="sm")
|
412 |
+
la_btn = gr.Button("🌴 LA", size="sm")
|
413 |
+
chicago_btn = gr.Button("🏢 Chicago", size="sm")
|
414 |
+
with gr.Row():
|
415 |
+
miami_btn = gr.Button("🏖️ Miami", size="sm")
|
416 |
+
denver_btn = gr.Button("⛰️ Denver", size="sm")
|
417 |
+
seattle_btn = gr.Button("🌲 Seattle", size="sm")
|
418 |
+
bozeman_btn = gr.Button("🏔️ Bozeman, MT", size="sm", variant="secondary")
|
419 |
+
|
420 |
gr.Markdown("""
|
421 |
+
### 📍 Manual Coordinates:
|
422 |
- **NYC**: 40.7128, -74.0060
|
423 |
- **LA**: 34.0522, -118.2437
|
424 |
- **Chicago**: 41.8781, -87.6298
|
425 |
- **Miami**: 25.7617, -80.1918
|
426 |
- **Denver**: 39.7392, -104.9903
|
427 |
- **Seattle**: 47.6062, -122.3321
|
428 |
+
- **Bozeman, MT**: 45.6770, -111.0429
|
429 |
""")
|
430 |
|
431 |
with gr.Column(scale=2):
|
|
|
477 |
update_btn.click(
|
478 |
fn=weather_app.update_location,
|
479 |
inputs=[lat_input, lon_input],
|
480 |
+
outputs=[map_html, lat_input, lon_input]
|
481 |
+
)
|
482 |
+
|
483 |
+
# City button event handlers
|
484 |
+
nyc_btn.click(
|
485 |
+
fn=lambda: weather_app.set_city_coordinates("New York City"),
|
486 |
+
outputs=[map_html, lat_input, lon_input]
|
487 |
+
)
|
488 |
+
la_btn.click(
|
489 |
+
fn=lambda: weather_app.set_city_coordinates("Los Angeles"),
|
490 |
+
outputs=[map_html, lat_input, lon_input]
|
491 |
+
)
|
492 |
+
chicago_btn.click(
|
493 |
+
fn=lambda: weather_app.set_city_coordinates("Chicago"),
|
494 |
+
outputs=[map_html, lat_input, lon_input]
|
495 |
+
)
|
496 |
+
miami_btn.click(
|
497 |
+
fn=lambda: weather_app.set_city_coordinates("Miami"),
|
498 |
+
outputs=[map_html, lat_input, lon_input]
|
499 |
+
)
|
500 |
+
denver_btn.click(
|
501 |
+
fn=lambda: weather_app.set_city_coordinates("Denver"),
|
502 |
+
outputs=[map_html, lat_input, lon_input]
|
503 |
+
)
|
504 |
+
seattle_btn.click(
|
505 |
+
fn=lambda: weather_app.set_city_coordinates("Seattle"),
|
506 |
+
outputs=[map_html, lat_input, lon_input]
|
507 |
+
)
|
508 |
+
bozeman_btn.click(
|
509 |
+
fn=lambda: weather_app.set_city_coordinates("Bozeman, MT"),
|
510 |
+
outputs=[map_html, lat_input, lon_input]
|
511 |
)
|
512 |
|
513 |
weather_btn.click(
|
|
|
520 |
lat_input.change(
|
521 |
fn=weather_app.update_location,
|
522 |
inputs=[lat_input, lon_input],
|
523 |
+
outputs=[map_html, lat_input, lon_input]
|
524 |
)
|
525 |
|
526 |
lon_input.change(
|
527 |
fn=weather_app.update_location,
|
528 |
inputs=[lat_input, lon_input],
|
529 |
+
outputs=[map_html, lat_input, lon_input]
|
530 |
)
|
531 |
|
532 |
# Launch the app
|