Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import plotly.graph_objects as go
|
3 |
+
from folium.raster_layers import ImageOverlay
|
4 |
+
import re
|
5 |
+
import glob
|
6 |
+
import pickle
|
7 |
+
import h5py
|
8 |
+
import rasterio
|
9 |
+
import streamlit as st
|
10 |
+
import os
|
11 |
+
import branca.colormap as cm
|
12 |
+
import folium
|
13 |
+
import numpy as np
|
14 |
+
import pandas as pd
|
15 |
+
from geopy.extra.rate_limiter import RateLimiter
|
16 |
+
from geopy.geocoders import Nominatim
|
17 |
+
from streamlit_plotly_events import plotly_events
|
18 |
+
import warnings
|
19 |
+
from folium import plugins
|
20 |
+
warnings.filterwarnings("ignore")
|
21 |
+
|
22 |
+
|
23 |
+
@st.cache_data
|
24 |
+
def convert_df(df):
|
25 |
+
return df.to_csv(index=0).encode('utf-8')
|
26 |
+
|
27 |
+
|
28 |
+
@st.cache_data
|
29 |
+
def geocode(address):
|
30 |
+
try:
|
31 |
+
address2 = address.replace(' ', '+').replace(',', '%2C')
|
32 |
+
df = pd.read_json(
|
33 |
+
f'https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address={address2}&benchmark=2020&format=json')
|
34 |
+
results = df.iloc[:1, 0][0][0]['coordinates']
|
35 |
+
lat, lon = results['y'], results['x']
|
36 |
+
except:
|
37 |
+
geolocator = Nominatim(user_agent="GTA Lookup")
|
38 |
+
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
|
39 |
+
location = geolocator.geocode(address)
|
40 |
+
lat, lon = location.latitude, location.longitude
|
41 |
+
return pd.DataFrame({'Lat': lat, 'Lon': lon}, index=[0])
|
42 |
+
|
43 |
+
|
44 |
+
@st.cache_data
|
45 |
+
def get_data(row, col, radius):
|
46 |
+
files = [
|
47 |
+
"data/GUST_hrrr_2021_all_max.h5",
|
48 |
+
"data/GUST_hrrr_2022_all_max.h5",
|
49 |
+
"data/mritchey/GUST_hrrr_2023_all_max.h5",
|
50 |
+
"data/GUST_hrrr_202401_202409_max.h5",
|
51 |
+
]
|
52 |
+
|
53 |
+
all_data = []
|
54 |
+
all_dates = []
|
55 |
+
for f in files:
|
56 |
+
with h5py.File(f, 'r') as f:
|
57 |
+
data = f['GUST'][:, row-radius:row +
|
58 |
+
radius+1, col-radius:col+radius+1]
|
59 |
+
dates = f['dates'][:]
|
60 |
+
all_data.append(data)
|
61 |
+
all_dates.append(dates)
|
62 |
+
|
63 |
+
data_mat = np.concatenate(all_data)
|
64 |
+
data_mat = np.where(data_mat < 0, 0, data_mat)*2.23694
|
65 |
+
|
66 |
+
dates_mat = np.concatenate(all_dates)
|
67 |
+
|
68 |
+
data_actual = np.array([i[radius, radius] for i in data_mat])
|
69 |
+
|
70 |
+
data_max = np.max(data_mat, axis=(1, 2))
|
71 |
+
data_max_2 = np.max(data_mat, axis=0)
|
72 |
+
data_max_2 = data_max_2
|
73 |
+
|
74 |
+
df = pd.DataFrame({'Date': dates_mat,
|
75 |
+
'Actual': data_actual,
|
76 |
+
'Max': data_max})
|
77 |
+
|
78 |
+
df['Date'] = pd.to_datetime(df['Date'], format='%Y%m%d')
|
79 |
+
# df['Date']=df['Date']+pd.Timedelta(days=1)
|
80 |
+
|
81 |
+
return df, data_max_2
|
82 |
+
|
83 |
+
|
84 |
+
def lat_lon_to_row_col(lat, lon):
|
85 |
+
crs_dic = pickle.load(open('data/hrrr_crs.pkl', 'rb'))
|
86 |
+
transform = crs_dic['affine']
|
87 |
+
trans_rtma = crs_dic['proj_4326']
|
88 |
+
lon_rtma, lat_rtma = trans_rtma.transform(lon, lat)
|
89 |
+
|
90 |
+
row, col = rasterio.transform.rowcol(transform, lon_rtma, lat_rtma)
|
91 |
+
row, col = int(row), int(col)
|
92 |
+
return row, col
|
93 |
+
|
94 |
+
|
95 |
+
def map_folium(lat, lon, files_dates_selected, actual_point, max_point):
|
96 |
+
popup_content = f"""
|
97 |
+
<div style="font-size: 7pt; width: 100px; height: 100px;">
|
98 |
+
<b>{address}</b><br>
|
99 |
+
<b>Gust: {actual_point:,.2f} MPH</b><br>
|
100 |
+
<b>Max: {max_point:,.2f} MPH</b><br>
|
101 |
+
</div>
|
102 |
+
"""
|
103 |
+
# Create a base map
|
104 |
+
m = folium.Map(location=[lat, lon], zoom_start=6)
|
105 |
+
folium.Marker(location=[lat, lon],
|
106 |
+
|
107 |
+
popup=folium.Popup(popup_content, max_width=400)
|
108 |
+
|
109 |
+
|
110 |
+
).add_to(m)
|
111 |
+
|
112 |
+
# Define the image bounds (SW and NE corners)
|
113 |
+
image_bounds = [[21.081227294744885, -134.02385805744265],
|
114 |
+
[52.62502506520796, -60.98015065638055]]
|
115 |
+
|
116 |
+
# Add ImageOverlays for each image
|
117 |
+
|
118 |
+
overlay = ImageOverlay(image=files_dates_selected, bounds=image_bounds,
|
119 |
+
opacity=.75,
|
120 |
+
mercator_project=False)
|
121 |
+
|
122 |
+
overlay.add_to(m)
|
123 |
+
colormap_hail = cm.LinearColormap(
|
124 |
+
colors=['blue', 'lightblue', 'pink', 'red'], vmin=0, vmax=75)
|
125 |
+
# Add the color legend to the map
|
126 |
+
colormap_hail.caption = 'Legend: Wind Gust (MPH)'
|
127 |
+
colormap_hail.add_to(m)
|
128 |
+
|
129 |
+
plugins.Fullscreen().add_to(m)
|
130 |
+
|
131 |
+
return m
|
132 |
+
|
133 |
+
|
134 |
+
def get_all_data(lat, lon, radius, start_date, end_date):
|
135 |
+
#Geocode and get Data
|
136 |
+
|
137 |
+
row, col = lat_lon_to_row_col(lat, lon)
|
138 |
+
|
139 |
+
df_data, max_values = get_data(row, col, radius)
|
140 |
+
|
141 |
+
df_data = df_data.query(f"'{start_date}'<=Date<='{end_date}'")
|
142 |
+
df_data['Actual'] = df_data['Actual'].astype(float).round(2)
|
143 |
+
df_data['Max'] = df_data['Max'].astype(float).round(2)
|
144 |
+
|
145 |
+
fig = go.Figure()
|
146 |
+
|
147 |
+
# Add bars for actual values
|
148 |
+
fig.add_trace(go.Bar(
|
149 |
+
x=df_data['Date'],
|
150 |
+
y=df_data['Actual'],
|
151 |
+
name='Actual Value',
|
152 |
+
marker_color='#2D5986',
|
153 |
+
hoverinfo='text', # Show text information only
|
154 |
+
text=df_data.apply(
|
155 |
+
lambda row: f'Date: {row["Date"].date()}<br>Gust: {row["Actual"]}<br>Max: {row["Max"]}', axis=1)
|
156 |
+
|
157 |
+
))
|
158 |
+
|
159 |
+
# Update layout
|
160 |
+
fig.update_layout(
|
161 |
+
title='',
|
162 |
+
xaxis_title='Date',
|
163 |
+
yaxis_title='Gust (MPH)',
|
164 |
+
barmode='group'
|
165 |
+
)
|
166 |
+
|
167 |
+
return fig, df_data
|
168 |
+
|
169 |
+
|
170 |
+
#Set up 2 Columns
|
171 |
+
st.set_page_config(layout="wide")
|
172 |
+
col1, col2 = st.columns((2))
|
173 |
+
|
174 |
+
os.chdir(r'C:\Users\mritchey')
|
175 |
+
|
176 |
+
#Input Values
|
177 |
+
address = st.sidebar.text_input("Address", "123 Main Street, Dallas, TX 75126")
|
178 |
+
date_focus = st.sidebar.date_input("Date", pd.Timestamp(2021, 7, 1))
|
179 |
+
within_days = st.sidebar.selectbox('Days Within', (90, 180, 365))
|
180 |
+
circle_radius = st.sidebar.selectbox('Box Radius (Miles)', (5, 10, 25))
|
181 |
+
interactive_map = st.sidebar.radio(
|
182 |
+
'Interactive Map (Lower Res)', (False, True))
|
183 |
+
|
184 |
+
|
185 |
+
start_date = date_focus+pd.Timedelta(days=-within_days)
|
186 |
+
end_date = date_focus+pd.Timedelta(days=within_days)
|
187 |
+
|
188 |
+
date_range = pd.date_range(start=start_date, end=end_date).strftime('%Y%m%d')
|
189 |
+
|
190 |
+
result = geocode(address)
|
191 |
+
lat, lon = result.values[0]
|
192 |
+
radius = int(np.ceil(circle_radius*1.6/2.5))
|
193 |
+
|
194 |
+
fig, df_data = get_all_data(lat, lon, radius, start_date, end_date)
|
195 |
+
|
196 |
+
_, actual_point, max_point = df_data.query(f"Date=='{date_focus}'").values[0]
|
197 |
+
|
198 |
+
files = glob.glob(
|
199 |
+
r'data\**\*.webp', recursive=True)
|
200 |
+
|
201 |
+
with col1:
|
202 |
+
st.title('Gust')
|
203 |
+
try:
|
204 |
+
|
205 |
+
selected_points = plotly_events(fig)
|
206 |
+
csv = convert_df(df_data)
|
207 |
+
st.download_button(
|
208 |
+
label="Download data as CSV",
|
209 |
+
data=csv,
|
210 |
+
file_name='data.csv',
|
211 |
+
mime='text/csv')
|
212 |
+
except:
|
213 |
+
pass
|
214 |
+
|
215 |
+
|
216 |
+
with col2:
|
217 |
+
st.title('GUST MAP')
|
218 |
+
|
219 |
+
if selected_points:
|
220 |
+
# Extract the details of the first selected point
|
221 |
+
selected_index = selected_points[0]['pointIndex']
|
222 |
+
selected_data = df_data.iloc[selected_index]
|
223 |
+
|
224 |
+
|
225 |
+
_, actual_point, max_point = df_data.query(
|
226 |
+
f"Date=='{selected_data['Date']}'").values[0]
|
227 |
+
files_dates_selected = [i for i in files if selected_data['Date'].strftime(
|
228 |
+
'%Y%m%d') in re.search(r'(\d{8})', i).group()][0]
|
229 |
+
m = map_folium(lat, lon, files_dates_selected, actual_point, max_point)
|
230 |
+
m.save("map_new.html")
|
231 |
+
|
232 |
+
st.write('Date: ' + selected_data['Date'].strftime('%m-%d-%Y'))
|
233 |
+
st.components.v1.html(
|
234 |
+
open("map_new.html", 'r').read(), height=500, width=500)
|
235 |
+
|
236 |
+
else:
|
237 |
+
files_dates_selected = [i for i in files if date_focus.strftime(
|
238 |
+
'%Y%m%d') in re.search(r'(\d{8})', i).group()][0]
|
239 |
+
st.write('Date: ' + date_focus.strftime('%m-%d-%Y'))
|
240 |
+
m = map_folium(lat, lon, files_dates_selected, actual_point, max_point)
|
241 |
+
m.save("map_new.html")
|
242 |
+
st.components.v1.html(
|
243 |
+
open("map_new.html", 'r').read(), height=500, width=500)
|
244 |
+
|
245 |
+
|
246 |
+
st.markdown(""" <style>
|
247 |
+
#MainMenu {visibility: hidden;}
|
248 |
+
footer {visibility: hidden;}
|
249 |
+
</style> """, unsafe_allow_html=True)
|