mattritchey commited on
Commit
14f49e4
1 Parent(s): ca47b0e

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py.py +257 -0
  2. requirements.txt +20 -0
app.py.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Fri Oct 14 10:35:25 2022
4
+
5
+ @author: mritchey
6
+ """
7
+ # streamlit run "C:\Users\mritchey\.spyder-py3\Python Scripts\streamlit projects\hrrr\windtrial2_streamlit_hrrr 2.py"
8
+ import base64
9
+ import datetime
10
+ import itertools
11
+
12
+ import branca.colormap as cm
13
+ import folium
14
+ import numpy as np
15
+ import pandas as pd
16
+ import plotly.express as px
17
+ import rasterio
18
+ import rioxarray
19
+ import s3fs
20
+
21
+ import streamlit as st
22
+ import xarray as xr
23
+ from geogif import gif
24
+ from geopy.extra.rate_limiter import RateLimiter
25
+ from geopy.geocoders import Nominatim
26
+ from joblib import Parallel, delayed
27
+ from matplotlib import colors as colors
28
+ from pyproj import CRS, Transformer
29
+ from streamlit_folium import st_folium
30
+
31
+
32
+ @st.cache
33
+ def convert_df(df):
34
+ # IMPORTANT: Cache the conversion to prevent computation on every rerun
35
+ return df.to_csv(index=0).encode('utf-8')
36
+
37
+ @st.cache
38
+ def geocode(address):
39
+ try:
40
+ address2 = address.replace(' ', '+').replace(',', '%2C')
41
+ df = pd.read_json(
42
+ f'https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address={address2}&benchmark=2020&format=json')
43
+ results = df.iloc[:1, 0][0][0]['coordinates']
44
+ lat, lon = results['y'], results['x']
45
+ except:
46
+ geolocator = Nominatim(user_agent="GTA Lookup")
47
+ geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
48
+ location = geolocator.geocode(address)
49
+ lat, lon = location.latitude, location.longitude
50
+ return lat, lon
51
+
52
+ @st.cache
53
+ def get_data_date_time(date_time, lat, lon):
54
+ date, time = date_time
55
+ path = f"hrrrzarr/sfc/{date}/{date}_{time[-2:]}z_{analysis_forecast}.zarr/{vertical_level}/{variable}"
56
+ try:
57
+ ds = xr.open_mfdataset([lookup(path), lookup(f"{path}/{vertical_level}")],
58
+ engine="zarr")
59
+ result = ds.sel(projection_x_coordinate=lon, projection_y_coordinate=lat, method="nearest")[
60
+ variable].values*2.23694
61
+ except:
62
+ result = None
63
+ date2 = pd.to_datetime(str(date)+str(time), format='%Y%m%d%H')
64
+ df = pd.DataFrame({'Date': date2, 'MPH': result}, index=[0])
65
+ return df
66
+
67
+ def mapvalue2color(value, cmap):
68
+
69
+ if np.isnan(value):
70
+ return (1, 0, 0, 0)
71
+ else:
72
+ return colors.to_rgba(cmap(value), 0.7)
73
+
74
+
75
+ def lookup(path):
76
+ return s3fs.S3Map(path, s3=s3)
77
+
78
+
79
+
80
+ s3 = s3fs.S3FileSystem(anon=True)
81
+
82
+ st.set_page_config(layout="wide")
83
+ col1, col2 = st.columns((2))
84
+
85
+ address = st.sidebar.text_input(
86
+ "Address", "123 Main Street, Columbus, OH 43215")
87
+ d = st.sidebar.date_input(
88
+ "Date", pd.Timestamp(2022, 9, 28)).strftime('%Y%m%d')
89
+
90
+ time = st.sidebar.selectbox('Time:', ('12 AM', '6 AM', '12 PM', '6 PM',))
91
+ type_wind = st.sidebar.selectbox('Type:', ('Gust', 'Wind'))
92
+ analysis_forecast1 = st.sidebar.radio(
93
+ 'Analysis or Forecast:', ('Analysis', 'Forecast'))
94
+ entire_day = st.sidebar.radio(
95
+ 'Graph Entire Day/Forecast (Takes a Bit):', ('No', 'Yes'))
96
+ animate_forecast = st.sidebar.radio(
97
+ 'Animate Forecast:', ('No', 'Yes'))
98
+
99
+
100
+ if analysis_forecast1 == 'Analysis':
101
+ analysis_forecast = 'anl'
102
+ else:
103
+ analysis_forecast = 'fcst'
104
+
105
+
106
+ if time[-2:] == 'PM' and int(time[:2].strip()) < 12:
107
+ t = datetime.time(int(time[:2].strip())+12, 00).strftime('%H')+'00'
108
+ elif time[-2:] == 'AM' and int(time[:2].strip()) == 12:
109
+ t = '0000'
110
+ else:
111
+ t = datetime.time(int(time[:2].strip()), 00).strftime('%H')+'00'
112
+
113
+ year, month, day = d[:4], d[4:6], d[6:8]
114
+
115
+ if type_wind == 'Gust':
116
+ variable = type_wind.upper()
117
+ vertical_level = 'surface'
118
+ else:
119
+ vertical_level = '10m_above_ground'
120
+ if analysis_forecast == 'anl':
121
+ variable = 'WIND_max_fcst'
122
+ else:
123
+ variable = 'WIND_1hr_max_fcst'
124
+
125
+ path = f"hrrrzarr/sfc/{d}/{d}_{t[:2]}z_{analysis_forecast}.zarr/{vertical_level}/{variable}"
126
+ ds = xr.open_mfdataset([lookup(path), lookup(f"{path}/{vertical_level}")],
127
+ engine="zarr")
128
+
129
+ lat, lon = geocode(address)
130
+
131
+ ds = ds.rename(projection_x_coordinate="x", projection_y_coordinate="y")
132
+ crs = CRS.from_cf({"grid_mapping_name": "lambert_conformal_conic",
133
+ "longitude_of_central_meridian": -97.5,
134
+ "latitude_of_projection_origin": 38.5,
135
+ "standard_parallel": 38.5})
136
+ ds = ds.rio.write_crs(crs, inplace=True)
137
+
138
+ ds[variable] = ds[variable].astype("float64")
139
+ projected = ds.rio.reproject("EPSG:4326")
140
+
141
+ if analysis_forecast == 'fcst':
142
+ projected_org = projected
143
+ projected = projected.sel(time=projected.time.values[0])
144
+ else:
145
+ pass
146
+
147
+ wind_mph = projected.sel(x=lon, y=lat, method="nearest")[
148
+ variable].values*2.23694
149
+
150
+
151
+ affine = projected.rio.transform()
152
+
153
+ rows, columns = rasterio.transform.rowcol(affine, lon, lat)
154
+
155
+ size = 40
156
+
157
+ projected2 = projected[variable][rows-size:rows+size, columns-size:columns+size]
158
+ img = projected2.values*2.23694
159
+
160
+ boundary = projected2.rio.bounds()
161
+ left, bottom, right, top = boundary
162
+
163
+ img[img < 0.0] = np.nan
164
+
165
+ clat = (bottom + top)/2
166
+ clon = (left + right)/2
167
+
168
+ vmin = np.floor(np.nanmin(img))
169
+ vmax = np.ceil(np.nanmax(img))
170
+
171
+ colormap = cm.LinearColormap(
172
+ colors=['blue', 'lightblue', 'red'], vmin=vmin, vmax=vmax)
173
+
174
+ m = folium.Map(location=[lat, lon], zoom_start=9, height=500)
175
+
176
+ folium.Marker(
177
+ location=[lat, lon],
178
+ popup=f"{wind_mph.round(2)} MPH").add_to(m)
179
+
180
+ folium.raster_layers.ImageOverlay(
181
+ image=img,
182
+ name='Wind Speed Map',
183
+ opacity=.8,
184
+ bounds=[[bottom, left], [top, right]],
185
+ colormap=lambda value: mapvalue2color(value, colormap)
186
+ ).add_to(m)
187
+
188
+
189
+ folium.LayerControl().add_to(m)
190
+ colormap.caption = 'Wind Speed: MPH'
191
+ m.add_child(colormap)
192
+
193
+ with col1:
194
+ st.title('HRRR Model')
195
+ st.write(f"{type_wind.title()} Speed: {wind_mph.round(2)} MPH at {time} UTC")
196
+ st_folium(m, height=500)
197
+
198
+
199
+ proj = Transformer.from_crs(4326, crs, always_xy=True)
200
+ lon2, lat2 = proj.transform(lon, lat)
201
+
202
+
203
+ if entire_day == 'Yes':
204
+ if analysis_forecast == 'anl':
205
+ times = [f'0{str(i)}'[-2:] for i in range(0, 24)]
206
+ dates_times = list(itertools.product([d], times))
207
+
208
+ results = Parallel(n_jobs=-1, prefer="threads")(
209
+ delayed(get_data_date_time)(i, lat2, lon2) for i in dates_times)
210
+
211
+ df_all = pd.concat(results)
212
+ df_all['MPH'] = df_all['MPH'].round(2)
213
+
214
+ else:
215
+ result = ds[variable].sel(x=lon2, y=lat2, method="nearest")*2.23694
216
+ df_all = result.to_dataframe()[variable].reset_index()
217
+ df_all.columns = ['Date', 'MPH']
218
+ df_all['MPH'] = df_all['MPH'].round(2)
219
+ if animate_forecast == 'Yes':
220
+ # Not working
221
+ gif(projected_org[variable], to='ds.gif',
222
+ date_format='%m-%d-%Y: %I%p', cmap="RdBu_r", vmax=35)
223
+ st.download_button(
224
+ label="Download Gif",
225
+ data='ds.gif',
226
+ file_name='ds.gif',
227
+ )
228
+ file_ = open("ds.gif", "rb")
229
+ contents = file_.read()
230
+ data_url = base64.b64encode(contents).decode("utf-8")
231
+ file_.close()
232
+
233
+ st.markdown(
234
+ f'<img src="data:image/gif;base64,{data_url}" alt="ds gif" width="1200">',
235
+ unsafe_allow_html=True,
236
+ )
237
+
238
+ fig = px.line(df_all, x="Date", y="MPH")
239
+ with col2:
240
+ st.title(f'{analysis_forecast1}')
241
+ st.plotly_chart(fig)
242
+
243
+ csv = convert_df(df_all)
244
+
245
+ st.download_button(
246
+ label="Download data as CSV",
247
+ data=csv,
248
+ file_name='data.csv',
249
+ mime='text/csv')
250
+
251
+ else:
252
+ pass
253
+
254
+ st.markdown(""" <style>
255
+ #MainMenu {visibility: hidden;}
256
+ footer {visibility: hidden;}
257
+ </style> """, unsafe_allow_html=True)
requirements.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ecmwflibs
2
+ zarr
3
+ cfgrib
4
+ branca==0.5.0
5
+ folium==0.12.1
6
+ geopy==2.2.0
7
+ geogif==0.1.3
8
+ joblib==1.1.0
9
+ matplotlib==3.5.1
10
+ numpy==1.21.5
11
+ pandas==1.4.2
12
+ plotly==5.7.0
13
+ pyproj==3.3.1
14
+ rasterio==1.2.10
15
+ rioxarray==0.12.2
16
+ s3fs==2022.10.0
17
+ streamlit==1.4.0
18
+ streamlit_folium==0.6.15
19
+ xarray[complete]
20
+ geogif==0.1.3