aromidvar1355 commited on
Commit
9c3f41a
ยท
verified ยท
1 Parent(s): 68e35c2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -117
app.py CHANGED
@@ -37,12 +37,19 @@ data_source = st.sidebar.selectbox(
37
  openweather_historical_readings = []
38
  openweather_forecast_readings = []
39
  stormglass_marine_readings = []
40
- solunar_tide_readings = []
41
-
42
 
43
  # Initialize session state for historical weather data
44
  if 'openweather_historical_readings' not in st.session_state:
45
- st.session_state.openweather_historical_readings = []
 
 
 
 
 
 
 
 
46
 
47
  # --- API CALLING FUNCTIONS ---
48
  def fetch_openweather_data(lat, lon, datetime_input):
@@ -124,33 +131,42 @@ def fetch_openweather_forecast(lat, lon):
124
 
125
  def fetch_stormglass_data(lat, lng, start_datetime, end_datetime, selected_params):
126
  """Fetches marine data from Stormglass API."""
127
- params_str = ','.join(selected_params)
128
- start_str = start_datetime.isoformat()
129
- end_str = end_datetime.isoformat()
130
- url = f"https://api.stormglass.io/v2/weather/point?lat={lat}&lng={lng}&params={params_str}&start={start_str}&end={end_str}"
131
- headers = {'Authorization': STORMGLASS_API_KEY}
132
  try:
 
 
 
 
 
 
133
  response = requests.get(url, headers=headers)
134
  response.raise_for_status()
135
  data = response.json()
136
 
137
- # Store the marine data in the global list
138
  if data and 'hours' in data:
139
  for hour_data in data['hours']:
140
  processed_entry = {
141
- 'timestamp': hour_data.get('time', None),
142
  'latitude': lat,
143
  'longitude': lng,
144
- 'wave_height': hour_data.get('waveHeight', {}).get('value', None),
145
- 'wind_speed': hour_data.get('windSpeed', {}).get('value', None),
146
- 'temperature': hour_data.get('temperature', {}).get('value', None),
147
- # Add other relevant fields
 
 
 
 
 
 
 
 
 
148
  }
149
- stormglass_marine_readings.append(processed_entry)
150
 
151
  return data
152
  except requests.exceptions.RequestException as e:
153
- st.error(f"Error fetching Stormglass data: {e}")
154
  return None
155
 
156
  def fetch_solunar_tide_data(lat, lon, datetime_input):
@@ -168,7 +184,7 @@ def fetch_solunar_tide_data(lat, lon, datetime_input):
168
 
169
  tide_data = tide_response.json() if tide_response.status_code == 200 else None
170
  astronomy_data = astronomy_response.json() if astronomy_response.status_code == 200 else None
171
- # Store the solunar and tide data in the global list
172
  if tide_data or astronomy_data:
173
  processed_entry = {
174
  'date': datetime_input.date().isoformat(),
@@ -177,17 +193,15 @@ def fetch_solunar_tide_data(lat, lon, datetime_input):
177
  'tide_data': tide_data,
178
  'astronomy_data': astronomy_data
179
  }
180
- solunar_tide_readings.append(processed_entry)
181
-
182
 
183
  return {
184
  'tide': tide_data,
185
  'astronomy': astronomy_data
186
  }
187
-
188
  except Exception as e:
189
  st.error(f"Error fetching Solunar and Tide data: {e}")
190
- return None
191
 
192
  def save_openweather_historical_to_csv():
193
  """Save historical OpenWeather data to CSV for download."""
@@ -405,7 +419,8 @@ def main():
405
  st.write(f"Humidity: {forecast['humidity']}% | Wind: {forecast['wind_speed']} mph")
406
 
407
  elif data_source == "Marine":
408
- st.title("Stormglass Marine & Weather Data")
 
409
 
410
  col1, col2 = st.columns(2)
411
 
@@ -472,54 +487,48 @@ def main():
472
  selected_params
473
  )
474
 
 
475
  if stormglass_data and 'hours' in stormglass_data:
476
  st.success(f"Retrieved {len(stormglass_data['hours'])} hours of data")
477
-
478
- # Process data
479
  processed_data = []
480
-
481
  for hour_data in stormglass_data['hours']:
482
  hour_record = {'time': hour_data['time']}
483
  for param in selected_params:
484
  if param in hour_data:
485
- # Calculate average across sources
486
  sources = hour_data[param]
487
  values = [v for k, v in sources.items() if v is not None]
488
-
489
  if values:
490
  hour_record[f"{param}_avg"] = sum(values) / len(values)
491
-
492
- # Also store individual sources
493
  for source, value in sources.items():
494
  if value is not None:
495
  hour_record[f"{param}_{source}"] = value
496
-
497
  processed_data.append(hour_record)
498
-
499
  sg_df = pd.DataFrame(processed_data)
500
  sg_df['time'] = pd.to_datetime(sg_df['time'])
501
-
502
- # Show raw data
503
  with st.expander("Show Raw Data"):
504
  st.dataframe(sg_df)
505
-
506
- # Create plots for each parameter
507
  for param in selected_params:
508
  avg_col = f"{param}_avg"
509
-
510
  if avg_col in sg_df.columns:
511
  param_sources = [col for col in sg_df.columns if col.startswith(f"{param}_") and col != avg_col]
512
-
513
- # Create main plot with average values
514
  fig = px.line(
515
  sg_df,
516
  x='time',
517
  y=avg_col,
518
  title=f"{param} (Average)",
519
- labels={'time': 'Time', avg_col: param}
520
  )
521
-
522
- # Add individual sources
523
  for source_col in param_sources:
524
  source_name = source_col.replace(f"{param}_", "")
525
  fig.add_scatter(
@@ -528,109 +537,130 @@ def main():
528
  mode='lines',
529
  name=source_name
530
  )
531
-
532
  st.plotly_chart(fig, use_container_width=True)
533
- else:
534
- st.error("No data available for the selected parameters")
535
-
536
- with col2:
537
- if 'stormglass_data' in locals() and stormglass_data and 'hours' in stormglass_data:
538
- st.subheader("Data Statistics")
539
-
540
- for param in selected_params:
541
- avg_col = f"{param}_avg"
542
-
543
- if avg_col in sg_df.columns:
544
- st.write(f"**{param}**")
545
-
546
- stats_col1, stats_col2 = st.columns(2)
547
-
548
- with stats_col1:
549
- st.metric("Min", f"{sg_df[avg_col].min():.2f}")
550
- st.metric("Max", f"{sg_df[avg_col].max():.2f}")
551
-
552
- with stats_col2:
553
- st.metric("Mean", f"{sg_df[avg_col].mean():.2f}")
554
- st.metric("Median", f"{sg_df[avg_col].median():.2f}")
555
-
556
- st.markdown("---")
557
-
558
- # Additional visualizations
559
- if 'waveHeight_avg' in sg_df.columns and 'windSpeed_avg' in sg_df.columns:
560
- st.subheader("Wave Height vs Wind Speed")
561
-
562
- scatter_fig = px.scatter(
563
- sg_df,
564
- x='windSpeed_avg',
565
- y='waveHeight_avg',
566
- title="Correlation: Wave Height vs Wind Speed",
567
- labels={'windSpeed_avg': 'Wind Speed', 'waveHeight_avg': 'Wave Height'}
568
- )
569
-
570
- st.plotly_chart(scatter_fig, use_container_width=True)
571
-
572
- # Time of day analysis if we have at least 24 hours of data
573
- if len(sg_df) >= 24:
574
- st.subheader("Time of Day Analysis")
575
-
576
- sg_df['hour'] = sg_df['time'].dt.hour
577
-
578
- for param in selected_params:
579
- avg_col = f"{param}_avg"
580
-
581
- if avg_col in sg_df.columns:
582
- hourly_avg = sg_df.groupby('hour')[avg_col].mean().reset_index()
583
-
584
- hour_fig = px.line(
585
- hourly_avg,
586
- x='hour',
587
- y=avg_col,
588
- title=f"{param} by Hour of Day",
589
- labels={'hour': 'Hour of Day', avg_col: param}
590
- )
591
-
592
- st.plotly_chart(hour_fig, use_container_width=True)
593
 
594
  elif data_source == "Solunar & Tide Data":
595
- st.header("๐ŸŒž Solunar & Tide Information")
596
-
597
- # Location input
 
598
  lat = st.number_input("Latitude", value=25.7617, step=0.001, format="%.4f")
599
  lon = st.number_input("Longitude", value=-80.1918, step=0.001, format="%.4f")
600
-
601
- # Date input
602
  input_date = st.date_input("Date", datetime.now())
603
  input_time = st.time_input("Time", datetime.time(datetime.now()))
604
  datetime_input = datetime.combine(input_date, input_time)
605
-
606
  if st.button("Retrieve Solunar and Tide Data"):
607
  solunar_tide_data = fetch_solunar_tide_data(lat, lon, datetime_input)
608
-
609
  if solunar_tide_data:
610
  # Tide Information
611
  if solunar_tide_data['tide'] and 'data' in solunar_tide_data['tide']:
612
  st.subheader("Tide Times")
613
  tide_df = pd.DataFrame(solunar_tide_data['tide']['data'])
614
-
615
  for _, tide in tide_df.iterrows():
616
  st.write(f"**{tide['type'].capitalize()} Tide:**")
617
- st.write(f"Time: {pd.to_datetime(tide['time']).strftime('%Y-%m-%d %H:%M:%S')}")
 
618
  st.write(f"Height: {tide.get('height', 'N/A')} meters")
619
-
620
  # Astronomy Information
621
  if solunar_tide_data['astronomy'] and 'data' in solunar_tide_data['astronomy']:
622
  st.subheader("Sun and Moon Information")
623
  astro_data = solunar_tide_data['astronomy']['data']
624
-
625
  col1, col2 = st.columns(2)
626
-
627
  with col1:
628
- st.metric("Sunrise", astro_data.get('sunrise', 'N/A'))
 
629
  st.metric("Solar Noon", astro_data.get('solarNoon', 'N/A'))
630
-
631
  with col2:
632
- st.metric("Sunset", astro_data.get('sunset', 'N/A'))
 
633
  st.metric("Day Length", astro_data.get('dayLength', 'N/A'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
 
635
  elif data_source == "Fish Categories Data":
636
  st.header("๐ŸŸ Fish Categires Data")
 
37
  openweather_historical_readings = []
38
  openweather_forecast_readings = []
39
  stormglass_marine_readings = []
40
+ solunar_tide_readings = []
 
41
 
42
  # Initialize session state for historical weather data
43
  if 'openweather_historical_readings' not in st.session_state:
44
+ st.session_state.openweather_historical_readings = []
45
+
46
+ # Initialize session state for marine data
47
+ if 'stormglass_marine_readings' not in st.session_state:
48
+ st.session_state.stormglass_marine_readings = []
49
+
50
+ # Initialize session state for solunar and tide data
51
+ if 'solunar_tide_readings' not in st.session_state:
52
+ st.session_state.solunar_tide_readings = []
53
 
54
  # --- API CALLING FUNCTIONS ---
55
  def fetch_openweather_data(lat, lon, datetime_input):
 
131
 
132
  def fetch_stormglass_data(lat, lng, start_datetime, end_datetime, selected_params):
133
  """Fetches marine data from Stormglass API."""
 
 
 
 
 
134
  try:
135
+ params_str = ','.join(selected_params)
136
+ start_str = start_datetime.isoformat()
137
+ end_str = end_datetime.isoformat()
138
+ url = f"https://api.stormglass.io/v2/weather/point?lat={lat}&lng={lng}&params={params_str}&start={start_str}&end={end_str}"
139
+ headers = {'Authorization': STORMGLASS_API_KEY}
140
+
141
  response = requests.get(url, headers=headers)
142
  response.raise_for_status()
143
  data = response.json()
144
 
 
145
  if data and 'hours' in data:
146
  for hour_data in data['hours']:
147
  processed_entry = {
148
+ 'timestamp': hour_data.get('time'),
149
  'latitude': lat,
150
  'longitude': lng,
151
+ 'waveHeight': hour_data.get('waveHeight', {}).get('value'),
152
+ 'wavePeriod': hour_data.get('wavePeriod', {}).get('value'),
153
+ 'waveDirection': hour_data.get('waveDirection', {}).get('value'),
154
+ 'windSpeed': hour_data.get('windSpeed', {}).get('value'),
155
+ 'windDirection': hour_data.get('windDirection', {}).get('value'),
156
+ 'airTemperature': hour_data.get('airTemperature', {}).get('value'),
157
+ 'waterTemperature': hour_data.get('waterTemperature', {}).get('value'),
158
+ 'seaLevel': hour_data.get('seaLevel', {}).get('value'),
159
+ 'humidity': hour_data.get('humidity', {}).get('value'),
160
+ 'precipitation': hour_data.get('precipitation', {}).get('value'),
161
+ 'visibility': hour_data.get('visibility', {}).get('value'),
162
+ 'currentSpeed': hour_data.get('currentSpeed', {}).get('value'),
163
+ 'currentDirection': hour_data.get('currentDirection', {}).get('value')
164
  }
165
+ st.session_state.stormglass_marine_readings.append(processed_entry)
166
 
167
  return data
168
  except requests.exceptions.RequestException as e:
169
+ st.error(f"Error fetching Stormglass marine data: {e}")
170
  return None
171
 
172
  def fetch_solunar_tide_data(lat, lon, datetime_input):
 
184
 
185
  tide_data = tide_response.json() if tide_response.status_code == 200 else None
186
  astronomy_data = astronomy_response.json() if astronomy_response.status_code == 200 else None
187
+
188
  if tide_data or astronomy_data:
189
  processed_entry = {
190
  'date': datetime_input.date().isoformat(),
 
193
  'tide_data': tide_data,
194
  'astronomy_data': astronomy_data
195
  }
196
+ st.session_state.solunar_tide_readings.append(processed_entry)
 
197
 
198
  return {
199
  'tide': tide_data,
200
  'astronomy': astronomy_data
201
  }
 
202
  except Exception as e:
203
  st.error(f"Error fetching Solunar and Tide data: {e}")
204
+ return None
205
 
206
  def save_openweather_historical_to_csv():
207
  """Save historical OpenWeather data to CSV for download."""
 
419
  st.write(f"Humidity: {forecast['humidity']}% | Wind: {forecast['wind_speed']} mph")
420
 
421
  elif data_source == "Marine":
422
+ st.title("Stormglass Marine & Weather Data")
423
+ st.subheader("Detailed Marine Weather Conditions and Forecasts")
424
 
425
  col1, col2 = st.columns(2)
426
 
 
487
  selected_params
488
  )
489
 
490
+ # After fetching data, process and display it
491
  if stormglass_data and 'hours' in stormglass_data:
492
  st.success(f"Retrieved {len(stormglass_data['hours'])} hours of data")
493
+
494
+ # Process data for display
495
  processed_data = []
 
496
  for hour_data in stormglass_data['hours']:
497
  hour_record = {'time': hour_data['time']}
498
  for param in selected_params:
499
  if param in hour_data:
 
500
  sources = hour_data[param]
501
  values = [v for k, v in sources.items() if v is not None]
 
502
  if values:
503
  hour_record[f"{param}_avg"] = sum(values) / len(values)
 
 
504
  for source, value in sources.items():
505
  if value is not None:
506
  hour_record[f"{param}_{source}"] = value
 
507
  processed_data.append(hour_record)
508
+
509
  sg_df = pd.DataFrame(processed_data)
510
  sg_df['time'] = pd.to_datetime(sg_df['time'])
511
+
512
+ # Show raw data in an expander
513
  with st.expander("Show Raw Data"):
514
  st.dataframe(sg_df)
515
+
516
+ # Create interactive visualizations
517
  for param in selected_params:
518
  avg_col = f"{param}_avg"
 
519
  if avg_col in sg_df.columns:
520
  param_sources = [col for col in sg_df.columns if col.startswith(f"{param}_") and col != avg_col]
521
+
522
+ # Main plot with average values
523
  fig = px.line(
524
  sg_df,
525
  x='time',
526
  y=avg_col,
527
  title=f"{param} (Average)",
528
+ labels={'time': 'Time', avg_col: param.capitalize()}
529
  )
530
+
531
+ # Add individual sources to the plot
532
  for source_col in param_sources:
533
  source_name = source_col.replace(f"{param}_", "")
534
  fig.add_scatter(
 
537
  mode='lines',
538
  name=source_name
539
  )
540
+
541
  st.plotly_chart(fig, use_container_width=True)
542
+
543
+ # Display statistics for each parameter
544
+ with st.expander("View Data Statistics"):
545
+ for param in selected_params:
546
+ avg_col = f"{param}_avg"
547
+ if avg_col in sg_df.columns:
548
+ stats = sg_df[avg_col].describe()
549
+ st.write(f"**Statistics for {param.capitalize()}:**")
550
+ st.write(f"Minimum: {stats['min']:.2f}")
551
+ st.write(f"Maximum: {stats['max']:.2f}")
552
+ st.write(f"Mean: {stats['mean']:.2f}")
553
+ st.write(f"Median: {stats['50%']:.2f}")
554
+ st.write(f"Standard Deviation: {stats['std']:.2f}")
555
+ st.markdown("---")
556
+
557
+ # Additional visualizations
558
+ if 'waveHeight_avg' in sg_df.columns and 'windSpeed_avg' in sg_df.columns:
559
+ st.subheader("Wave Height vs Wind Speed")
560
+ scatter_fig = px.scatter(
561
+ sg_df,
562
+ x='windSpeed_avg',
563
+ y='waveHeight_avg',
564
+ title="Correlation: Wave Height vs Wind Speed",
565
+ labels={'windSpeed_avg': 'Wind Speed', 'waveHeight_avg': 'Wave Height'}
566
+ )
567
+ st.plotly_chart(scatter_fig, use_container_width=True)
568
+
569
+ # Time of day analysis if sufficient data is available
570
+ if len(sg_df) >= 24:
571
+ st.subheader("Time of Day Analysis")
572
+ sg_df['hour'] = sg_df['time'].dt.hour
573
+
574
+ for param in selected_params:
575
+ avg_col = f"{param}_avg"
576
+ if avg_col in sg_df.columns:
577
+ hourly_avg = sg_df.groupby('hour')[avg_col].mean().reset_index()
578
+ hour_fig = px.line(
579
+ hourly_avg,
580
+ x='hour',
581
+ y=avg_col,
582
+ title=f"{param.capitalize()} by Hour of Day",
583
+ labels={'hour': 'Hour of Day', avg_col: param.capitalize()}
584
+ )
585
+ st.plotly_chart(hour_fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
 
587
  elif data_source == "Solunar & Tide Data":
588
+ st.header("๐ŸŒž Solunar and Tide Information")
589
+ st.subheader("Detailed Solunar and Tide Conditions")
590
+
591
+ # Location and date/time input
592
  lat = st.number_input("Latitude", value=25.7617, step=0.001, format="%.4f")
593
  lon = st.number_input("Longitude", value=-80.1918, step=0.001, format="%.4f")
594
+
 
595
  input_date = st.date_input("Date", datetime.now())
596
  input_time = st.time_input("Time", datetime.time(datetime.now()))
597
  datetime_input = datetime.combine(input_date, input_time)
598
+
599
  if st.button("Retrieve Solunar and Tide Data"):
600
  solunar_tide_data = fetch_solunar_tide_data(lat, lon, datetime_input)
601
+
602
  if solunar_tide_data:
603
  # Tide Information
604
  if solunar_tide_data['tide'] and 'data' in solunar_tide_data['tide']:
605
  st.subheader("Tide Times")
606
  tide_df = pd.DataFrame(solunar_tide_data['tide']['data'])
607
+
608
  for _, tide in tide_df.iterrows():
609
  st.write(f"**{tide['type'].capitalize()} Tide:**")
610
+ tide_time = pd.to_datetime(tide['time'])
611
+ st.write(f"Time: {tide_time.strftime('%Y-%m-%d %H:%M:%S')}")
612
  st.write(f"Height: {tide.get('height', 'N/A')} meters")
613
+
614
  # Astronomy Information
615
  if solunar_tide_data['astronomy'] and 'data' in solunar_tide_data['astronomy']:
616
  st.subheader("Sun and Moon Information")
617
  astro_data = solunar_tide_data['astronomy']['data']
618
+
619
  col1, col2 = st.columns(2)
 
620
  with col1:
621
+ sunrise_time = pd.to_datetime(astro_data.get('sunrise'))
622
+ st.metric("Sunrise", sunrise_time.strftime('%H:%M') if sunrise_time else 'N/A')
623
  st.metric("Solar Noon", astro_data.get('solarNoon', 'N/A'))
 
624
  with col2:
625
+ sunset_time = pd.to_datetime(astro_data.get('sunset'))
626
+ st.metric("Sunset", sunset_time.strftime('%H:%M') if sunset_time else 'N/A')
627
  st.metric("Day Length", astro_data.get('dayLength', 'N/A'))
628
+
629
+ # Additional astronomy details
630
+ # Visualization for moon phase
631
+ if 'moonPhase' in astro_data:
632
+ st.subheader("Moon Phase Visualization")
633
+ moon_phase = astro_data['moonPhase']
634
+ fig = go.Figure(go.Indicator(
635
+ mode = "gauge+number+delta",
636
+ value = moon_phase,
637
+ title = {'text': "Moon Phase"},
638
+ delta = {'reference': 0.5, 'increasing': {'color': "RebeccaPurple"}},
639
+ gauge = {
640
+ 'axis': {'range': [None, 1], 'visible': False},
641
+ 'bar': {'color': "lightgray"},
642
+ 'bgcolor': "lightgray",
643
+ 'borderwidth': 0,
644
+ 'width': 0.25
645
+ }
646
+ ))
647
+ st.plotly_chart(fig, use_container_width=True)
648
+
649
+ # Visualization for tide levels
650
+ if solunar_tide_data['tide'] and 'data' in solunar_tide_data['tide']:
651
+ st.subheader("Tide Level Visualization")
652
+ tide_df = pd.DataFrame(solunar_tide_data['tide']['data'])
653
+ tide_df['time'] = pd.to_datetime(tide_df['time'])
654
+ tide_df['height'] = pd.to_numeric(tide_df['height'], errors='coerce')
655
+
656
+ tide_fig = px.line(
657
+ tide_df,
658
+ x='time',
659
+ y='height',
660
+ title="Tide Levels Over Time",
661
+ labels={'time': 'Time', 'height': 'Tide Height (m)'}
662
+ )
663
+ st.plotly_chart(tide_fig, use_container_width=True)
664
 
665
  elif data_source == "Fish Categories Data":
666
  st.header("๐ŸŸ Fish Categires Data")