I open a dataset on the fly and use it plot using Matplotlib. The idea is to allow to explore and then click download once deployed.
class GeopotentialHeightAndTemperature(param.Parameterized):
def __init__(self):
self.row = pn.Column("init")
self.float_panel = pn.Column(pn.Spacer(height=0))
self.level_sel = RadioButtonGroup(
options=LEVELS,
value=LEVELS[-1],
button_style="outline",
button_type="success"
)
self.loc_widg = RadioButtonGroup(
options=DOMAINS,
value=DOMAINS["Canada"],
button_style="outline",
button_type="success",
)
self.modelrun_sel = Select(options=generate_model_runs_dict(), button_type="success")
self.model_sel = RadioButtonGroup(
options=models,
orientation="vertical",
button_style="outline",
button_type="success",
)
self.download_button = Button(icon="download", button_type="success")
self.load_button = Button(icon="brand-airtable", button_type="success")
self.ds, self.crs = self.load_data()
self.forecast_widg = DiscretePlayer(
name="Time",
value=self.ds.forecast.values[0],
options=list(self.ds.forecast.values),
width=350,
interval=2000,
)
self.file_download = pn.widgets.FileDownload(
callback=self._download_callback
)
self._overlay("init")
self.forecast_widg.param.watch(self._overlay, "value")
self.load_button.on_click(self._load_button_callback)
self.download_button.on_click(self._download_callback)
def _download_callback(self, event: str):
# hv.save(self.row[0], 'your_figure_name.png', backend='matplotlib', bbox_inches='tight')
# self.row[0].object.savefig('your_figure_name.png', bbox_inches='tight')
# self.row.append(pn.pane.Str("Test"))
# display(self.row[0].object)
# print(type(self.row[0]))
buf = BytesIO()
self.row[0].object.savefig(buf, 'file.png')
buf.seek(0)
return buf
def _load_button_callback(self, event: str):
self.ds, self.crs = self.load_data()
self._overlay("updated dataset")
def get_model_key(self):
current_value = self.model_sel.value
current_key = None
for key, value in models.items():
if value == current_value:
current_key = key
break
return current_key
def get_current_loc_key(self):
current_value = self.loc_widg.value
current_key = None
for key, value in DOMAINS.items():
if value == current_value:
current_key = key
break
return current_key
def load_data(self):
ds_temp = fstd2nc.Buffer(
sorted(Path(self.model_sel.value).glob(f"{self.modelrun_sel.value}_*")),
vars=["TT", "GZ"],
filter=[f"ip1=={int(self.level_sel.value)}"],
forecast_axis=True,
).to_xarray()
ds_temp = ds_temp.assign_coords(lon=(((ds_temp.lon + 180) % 360) - 180))
return ds_temp.isel(time=0, pres=0), get_crs(ds_temp)
def _matplotlib(self):
"""
Create a Matplotlib plot.
"""
data = self.ds.sel(
forecast=self.forecast_widg.value,
lon=slice(*self.loc_widg.value[:2]),
lat=slice(*self.loc_widg.value[-2:]),
).copy()
proj = self.crs
TITLE_EN = f"Temperature (°C) + Geopotential Height {self.level_sel.value}mb:"
TITLE_FR = (
f"Température (°C) + Hauteur Geopotentielle {self.level_sel.value}mb:"
)
formatted_forecast_hour = str(
int(self.forecast_widg.value / np.timedelta64(3600, "s"))
).zfill(3)
date_str = f"Init: {self.modelrun_sel.value[:4]}-{self.modelrun_sel.value[4:6]}-{self.modelrun_sel.value[6:8]} {self.modelrun_sel.value[-2:]}Z Forecast Hour:[{formatted_forecast_hour}] valid: TODO Z"
title = f'{self.get_model_key().replace("_", " ")} - {self.get_current_loc_key()}\n{TITLE_FR}\n{TITLE_EN}\n{date_str}'
fig, ax = plt.subplots(subplot_kw=dict(projection=self.crs))
# Map features
utils.set_limits(ax, self.crs, self.loc_widg.value)
wind = ax.contourf(
data.lon,
data.lat,
data.TT,
levels=6,
cmap="RdBu",
transform=self.crs,
)
pres = ax.contour(
data.lon,
data.lat,
data.GZ,
colors="black",
transform=self.crs,
linewidths=0.8,
)
utils.add_features(ax)
plt.title(title, fontsize=10)
# Labels on pressure lines
clabels = ax.clabel(pres, fmt="%1.0f", fontsize=3.5)
for label in clabels:
label.set_bbox(dict(facecolor="white", edgecolor="none", pad=0.6))
fig.colorbar(wind, fraction=0.035)
plt.close(fig)
return fig
def _overlay(self, event: str):
"""
Overlay method to switch between Holoviews and Matplotlib backends.
"""
map_plot = pn.pane.Matplotlib(
self._matplotlib(),
format="svg",
tight=True,
fixed_aspect=True,
sizing_mode="stretch_both",
# loading=True,
)
if self.row[0] is not None:
self.row.clear()
self.row.append(map_plot)
app = GeopotentialHeightAndTemperature()
template = pn.template.MaterialTemplate(
header_background="#78eb96",
logo="https://www.canada.ca/etc/designs/canada/wet-boew/assets/sig-blk-en.svg",
site="CMC",
title="Dynamic Geopotential Height and Temperature",
sidebar=[
app.model_sel,
app.modelrun_sel,
app.level_sel,
app.loc_widg,
app.load_button,
app.forecast_widg,
app.download_button,
app.file_download
],
main=[app.row],
sidebar_width=400,
).servable()