nakas commited on
Commit
4f20247
·
verified ·
1 Parent(s): 767d783

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -61
app.py CHANGED
@@ -9,6 +9,9 @@ import os
9
  import subprocess
10
  import sys
11
  import matplotlib.pyplot as plt
 
 
 
12
 
13
  # Install Playwright browsers on startup
14
  def install_playwright_browsers():
@@ -25,53 +28,49 @@ def install_playwright_browsers():
25
  except Exception as e:
26
  print(f"Error installing browsers: {e}")
27
 
28
- # Install browsers when the module loads
29
  install_playwright_browsers()
30
 
 
 
 
 
 
 
 
 
 
 
31
  def scrape_weather_data(site_id, hours=720):
32
  """Scrape weather data from weather.gov timeseries"""
33
  url = f"https://www.weather.gov/wrh/timeseries?site={site_id}&hours={hours}&units=english&chart=on&headers=on&obs=tabular&hourly=false&pview=full&font=12&plot="
34
 
35
  with sync_playwright() as p:
36
- # Launch browser with minimal settings
37
  browser = p.chromium.launch(
38
  headless=True,
39
  args=['--no-sandbox', '--disable-dev-shm-usage']
40
  )
41
-
42
- # Create context with desktop user agent
43
  context = browser.new_context(
44
- user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
45
  )
46
-
47
- # Create new page and navigate
48
  page = context.new_page()
49
  page.goto(url)
50
-
51
- # Wait for content to load
52
  time.sleep(5)
53
 
54
- # Get all text content
55
  content = page.evaluate('''() => {
56
- // Function to get all text content
57
  const getTextContent = () => {
58
- const rows = [];
59
  const tables = document.getElementsByTagName('table');
60
  for (const table of tables) {
61
  if (table.textContent.includes('Date/Time')) {
62
  const headerRow = Array.from(table.querySelectorAll('th'))
63
  .map(th => th.textContent.trim());
64
-
65
  const dataRows = Array.from(table.querySelectorAll('tbody tr'))
66
  .map(row => Array.from(row.querySelectorAll('td'))
67
  .map(td => td.textContent.trim()));
68
-
69
  return {headers: headerRow, rows: dataRows};
70
  }
71
  }
72
  return null;
73
  };
74
-
75
  return getTextContent();
76
  }''')
77
 
@@ -83,22 +82,23 @@ def parse_weather_data(data):
83
  if not data or 'rows' not in data:
84
  raise ValueError("No valid weather data found")
85
 
86
- # Convert to DataFrame
87
  df = pd.DataFrame(data['rows'])
88
 
89
- # Assign column names (first 8 columns we care about)
90
- columns = ['datetime', 'temp', 'dew_point', 'humidity', 'wind_chill', 'wind_dir', 'wind_speed', 'snow_depth']
91
- df = df.iloc[:, :8]
 
 
 
92
  df.columns = columns
93
 
94
  # Convert numeric columns
95
- df['temp'] = pd.to_numeric(df['temp'], errors='coerce')
96
- df['dew_point'] = pd.to_numeric(df['dew_point'], errors='coerce')
97
- df['humidity'] = pd.to_numeric(df['humidity'], errors='coerce')
98
- df['wind_chill'] = pd.to_numeric(df['wind_chill'], errors='coerce')
99
- df['snow_depth'] = pd.to_numeric(df['snow_depth'], errors='coerce')
100
 
101
- # Parse wind speed and gusts
102
  def parse_wind(x):
103
  if pd.isna(x): return np.nan, np.nan
104
  match = re.search(r'(\d+)G(\d+)', str(x))
@@ -110,47 +110,88 @@ def parse_weather_data(data):
110
  df['wind_speed'] = wind_data.apply(lambda x: x[0])
111
  df['wind_gust'] = wind_data.apply(lambda x: x[1])
112
 
 
 
 
 
 
 
 
113
  return df
114
 
115
- def create_temperature_plot(df):
116
- """Create temperature plot"""
117
- plt.figure(figsize=(12, 6))
118
- plt.plot(range(len(df)), df['temp'], label='Temperature', color='red')
119
- plt.plot(range(len(df)), df['wind_chill'], label='Wind Chill', color='blue')
120
- plt.title('Temperature and Wind Chill Over Time')
121
- plt.xlabel('Time')
122
- plt.ylabel('Temperature (°F)')
123
- plt.legend()
124
- # Set x-axis ticks
125
- x_ticks = np.linspace(0, len(df)-1, 10, dtype=int)
126
- plt.xticks(x_ticks, df['datetime'].iloc[x_ticks], rotation=45)
127
- plt.tight_layout()
128
- return plt
129
 
130
- def create_wind_plot(df):
131
- """Create wind plot"""
132
- plt.figure(figsize=(12, 6))
133
- plt.plot(range(len(df)), df['wind_speed'], label='Wind Speed', color='blue')
134
- plt.plot(range(len(df)), df['wind_gust'], label='Wind Gust', color='orange')
135
- plt.title('Wind Speed and Gusts Over Time')
136
- plt.xlabel('Time')
137
- plt.ylabel('Wind Speed (mph)')
138
- plt.legend()
139
- # Set x-axis ticks
140
- x_ticks = np.linspace(0, len(df)-1, 10, dtype=int)
141
- plt.xticks(x_ticks, df['datetime'].iloc[x_ticks], rotation=45)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  plt.tight_layout()
143
- return plt
 
144
 
145
  def analyze_weather_data(site_id, hours):
146
  """Analyze weather data and create visualizations"""
147
  try:
148
- # Scrape data
149
  raw_data = scrape_weather_data(site_id, hours)
150
  if not raw_data:
151
  return "Error: Could not retrieve weather data.", None, None
152
 
153
- # Parse data
154
  df = parse_weather_data(raw_data)
155
 
156
  # Calculate statistics
@@ -160,7 +201,8 @@ def analyze_weather_data(site_id, hours):
160
  'Max Wind Speed': f"{df['wind_speed'].max():.1f} mph",
161
  'Max Wind Gust': f"{df['wind_gust'].max():.1f} mph",
162
  'Average Humidity': f"{df['humidity'].mean():.1f}%",
163
- 'Max Snow Depth': f"{df['snow_depth'].max():.1f} inches"
 
164
  }
165
 
166
  # Create HTML output
@@ -171,10 +213,9 @@ def analyze_weather_data(site_id, hours):
171
  html_output += "</div>"
172
 
173
  # Create plots
174
- temp_fig = create_temperature_plot(df)
175
- wind_fig = create_wind_plot(df)
176
 
177
- return html_output, temp_fig, wind_fig
178
 
179
  except Exception as e:
180
  return f"Error analyzing data: {str(e)}", None, None
@@ -209,13 +250,13 @@ with gr.Blocks(title="Weather Station Data Analyzer") as demo:
209
  stats_output = gr.HTML(label="Statistics")
210
 
211
  with gr.Row():
212
- temp_plot = gr.Plot(label="Temperature Plot")
213
- wind_plot = gr.Plot(label="Wind Plot")
214
 
215
  analyze_btn.click(
216
  fn=analyze_weather_data,
217
  inputs=[site_id, hours],
218
- outputs=[stats_output, temp_plot, wind_plot]
219
  )
220
 
221
  if __name__ == "__main__":
 
9
  import subprocess
10
  import sys
11
  import matplotlib.pyplot as plt
12
+ from matplotlib.gridspec import GridSpec
13
+ import matplotlib.dates as mdates
14
+ from windrose import WindroseAxes
15
 
16
  # Install Playwright browsers on startup
17
  def install_playwright_browsers():
 
28
  except Exception as e:
29
  print(f"Error installing browsers: {e}")
30
 
 
31
  install_playwright_browsers()
32
 
33
+ def parse_direction(direction):
34
+ """Convert wind direction string to degrees"""
35
+ direction_map = {
36
+ 'N': 0, 'NNE': 22.5, 'NE': 45, 'ENE': 67.5,
37
+ 'E': 90, 'ESE': 112.5, 'SE': 135, 'SSE': 157.5,
38
+ 'S': 180, 'SSW': 202.5, 'SW': 225, 'WSW': 247.5,
39
+ 'W': 270, 'WNW': 292.5, 'NW': 315, 'NNW': 337.5
40
+ }
41
+ return direction_map.get(direction, np.nan)
42
+
43
  def scrape_weather_data(site_id, hours=720):
44
  """Scrape weather data from weather.gov timeseries"""
45
  url = f"https://www.weather.gov/wrh/timeseries?site={site_id}&hours={hours}&units=english&chart=on&headers=on&obs=tabular&hourly=false&pview=full&font=12&plot="
46
 
47
  with sync_playwright() as p:
 
48
  browser = p.chromium.launch(
49
  headless=True,
50
  args=['--no-sandbox', '--disable-dev-shm-usage']
51
  )
 
 
52
  context = browser.new_context(
53
+ user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
54
  )
 
 
55
  page = context.new_page()
56
  page.goto(url)
 
 
57
  time.sleep(5)
58
 
 
59
  content = page.evaluate('''() => {
 
60
  const getTextContent = () => {
 
61
  const tables = document.getElementsByTagName('table');
62
  for (const table of tables) {
63
  if (table.textContent.includes('Date/Time')) {
64
  const headerRow = Array.from(table.querySelectorAll('th'))
65
  .map(th => th.textContent.trim());
 
66
  const dataRows = Array.from(table.querySelectorAll('tbody tr'))
67
  .map(row => Array.from(row.querySelectorAll('td'))
68
  .map(td => td.textContent.trim()));
 
69
  return {headers: headerRow, rows: dataRows};
70
  }
71
  }
72
  return null;
73
  };
 
74
  return getTextContent();
75
  }''')
76
 
 
82
  if not data or 'rows' not in data:
83
  raise ValueError("No valid weather data found")
84
 
 
85
  df = pd.DataFrame(data['rows'])
86
 
87
+ # Get all relevant columns
88
+ columns = ['datetime', 'temp', 'dew_point', 'humidity', 'wind_chill',
89
+ 'wind_dir', 'wind_speed', 'snow_depth', 'snowfall_3hr',
90
+ 'snowfall_6hr', 'snowfall_24hr']
91
+
92
+ df = df.iloc[:, :11] # Take first 11 columns
93
  df.columns = columns
94
 
95
  # Convert numeric columns
96
+ numeric_cols = ['temp', 'dew_point', 'humidity', 'wind_chill', 'snow_depth',
97
+ 'snowfall_3hr', 'snowfall_6hr', 'snowfall_24hr']
98
+ for col in numeric_cols:
99
+ df[col] = pd.to_numeric(df[col], errors='coerce')
 
100
 
101
+ # Parse wind data
102
  def parse_wind(x):
103
  if pd.isna(x): return np.nan, np.nan
104
  match = re.search(r'(\d+)G(\d+)', str(x))
 
110
  df['wind_speed'] = wind_data.apply(lambda x: x[0])
111
  df['wind_gust'] = wind_data.apply(lambda x: x[1])
112
 
113
+ # Convert wind directions to degrees
114
+ df['wind_dir_deg'] = df['wind_dir'].apply(parse_direction)
115
+
116
+ # Convert datetime
117
+ df['datetime'] = pd.to_datetime(df['datetime'])
118
+ df['date'] = df['datetime'].dt.date
119
+
120
  return df
121
 
122
+ def create_wind_rose(df, ax):
123
+ """Create a wind rose plot"""
124
+ if not isinstance(ax, WindroseAxes):
125
+ ax = WindroseAxes.from_ax(ax=ax)
126
+ ax.bar(df['wind_dir_deg'], df['wind_speed'], bins=np.arange(0, 40, 5),
127
+ normed=True, opening=0.8, edgecolor='white')
128
+ ax.set_legend(title='Wind Speed (mph)')
129
+ ax.set_title('Wind Rose')
 
 
 
 
 
 
130
 
131
+ def create_plots(df):
132
+ """Create all weather plots"""
133
+ # Create figure with subplots
134
+ fig = plt.figure(figsize=(20, 15))
135
+ gs = GridSpec(3, 2, figure=fig)
136
+
137
+ # Temperature plot
138
+ ax1 = fig.add_subplot(gs[0, :])
139
+ ax1.plot(df['datetime'], df['temp'], label='Temperature', color='red')
140
+ ax1.plot(df['datetime'], df['wind_chill'], label='Wind Chill', color='blue')
141
+ ax1.set_title('Temperature and Wind Chill Over Time')
142
+ ax1.set_xlabel('Date')
143
+ ax1.set_ylabel('Temperature (°F)')
144
+ ax1.legend()
145
+ ax1.grid(True)
146
+ plt.setp(ax1.xaxis.get_majorticklabels(), rotation=45)
147
+
148
+ # Wind speed plot
149
+ ax2 = fig.add_subplot(gs[1, :])
150
+ ax2.plot(df['datetime'], df['wind_speed'], label='Wind Speed', color='blue')
151
+ ax2.plot(df['datetime'], df['wind_gust'], label='Wind Gust', color='orange')
152
+ ax2.set_title('Wind Speed and Gusts Over Time')
153
+ ax2.set_xlabel('Date')
154
+ ax2.set_ylabel('Wind Speed (mph)')
155
+ ax2.legend()
156
+ ax2.grid(True)
157
+ plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45)
158
+
159
+ # Snow depth plot
160
+ ax3 = fig.add_subplot(gs[2, 0])
161
+ ax3.plot(df['datetime'], df['snow_depth'], color='blue', label='Snow Depth')
162
+ ax3.set_title('Snow Depth Over Time')
163
+ ax3.set_xlabel('Date')
164
+ ax3.set_ylabel('Snow Depth (inches)')
165
+ ax3.grid(True)
166
+ plt.setp(ax3.xaxis.get_majorticklabels(), rotation=45)
167
+
168
+ # Daily new snow bar plot
169
+ ax4 = fig.add_subplot(gs[2, 1])
170
+ daily_snow = df.groupby('date')['snowfall_24hr'].max().reset_index()
171
+ ax4.bar(daily_snow['date'], daily_snow['snowfall_24hr'], color='blue')
172
+ ax4.set_title('Daily New Snow')
173
+ ax4.set_xlabel('Date')
174
+ ax4.set_ylabel('New Snow (inches)')
175
+ plt.setp(ax4.xaxis.get_majorticklabels(), rotation=45)
176
+
177
+ plt.tight_layout()
178
+
179
+ # Create separate wind rose figure
180
+ fig_rose = plt.figure(figsize=(10, 10))
181
+ ax_rose = WindroseAxes.from_ax(fig=fig_rose)
182
+ create_wind_rose(df, ax_rose)
183
  plt.tight_layout()
184
+
185
+ return fig, fig_rose
186
 
187
  def analyze_weather_data(site_id, hours):
188
  """Analyze weather data and create visualizations"""
189
  try:
190
+ # Scrape and parse data
191
  raw_data = scrape_weather_data(site_id, hours)
192
  if not raw_data:
193
  return "Error: Could not retrieve weather data.", None, None
194
 
 
195
  df = parse_weather_data(raw_data)
196
 
197
  # Calculate statistics
 
201
  'Max Wind Speed': f"{df['wind_speed'].max():.1f} mph",
202
  'Max Wind Gust': f"{df['wind_gust'].max():.1f} mph",
203
  'Average Humidity': f"{df['humidity'].mean():.1f}%",
204
+ 'Current Snow Depth': f"{df['snow_depth'].iloc[0]:.1f} inches",
205
+ 'Total New Snow (24hr)': f"{df['snowfall_24hr'].sum():.1f} inches"
206
  }
207
 
208
  # Create HTML output
 
213
  html_output += "</div>"
214
 
215
  # Create plots
216
+ main_plots, wind_rose = create_plots(df)
 
217
 
218
+ return html_output, main_plots, wind_rose
219
 
220
  except Exception as e:
221
  return f"Error analyzing data: {str(e)}", None, None
 
250
  stats_output = gr.HTML(label="Statistics")
251
 
252
  with gr.Row():
253
+ weather_plots = gr.Plot(label="Weather Plots")
254
+ wind_rose = gr.Plot(label="Wind Rose")
255
 
256
  analyze_btn.click(
257
  fn=analyze_weather_data,
258
  inputs=[site_id, hours],
259
+ outputs=[stats_output, weather_plots, wind_rose]
260
  )
261
 
262
  if __name__ == "__main__":