Spaces:
Runtime error
Runtime error
mattritchey
commited on
Commit
•
14f49e4
1
Parent(s):
ca47b0e
Upload 2 files
Browse files- app.py.py +257 -0
- 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
|