Button to refresh loaded Xarray.Dataset from multiple NetCDF files

Thank you! I managed to package my code into a class, and the refresh button now works as intended! Here is the full code.

from datetime import datetime, date
import glob

import geoviews as gv
import holoviews as hv
from holoviews.operation.datashader import rasterize
import numpy as np
import panel as pn
import param
from shapely.geometry import Point
import xarray as xr

## LOAD AND PREPARING DATA
#ds = xr.open_dataset('/usr/src/app/test_raytheon.nc')
# ns = 1e-9 # number of seconds in a nanosecond
# delta = timedelta(days=1, minutes=15)
# latest = datetime.utcfromtimestamp(ds['time'].values[0].astype(int) * ns)
# start = latest - delta
# date_slider = pn.widgets.DateSlider(name='Timestamp', start=start, end=latest, value=latest)

today = date.today().strftime("%Y%m%d")
files = sorted(glob.glob(f'/data/{today}*.nc'))
#files = sorted(glob.glob(f'2021*.nc'))
ds = xr.open_mfdataset(files)

var_list = ["Reflectivity", "SpecificDifferentialPhase", 'CorrectedReflectivity','DifferentialReflectivity', 'CorrectedDifferentialReflectivity', 'CrossPolCorrelation', 'Velocity']

angle_list = list(ds['scan_angle'].values)

# Package things into class:
class SkylerApp(param.Parameterized):
    # param depends on
    var_selected = param.ObjectSelector(default='Reflectivity', objects=var_list)
    scan_angle = param.ObjectSelector(default=2.0, objects=angle_list)
    timestep = param.ObjectSelector(default = -1, objects=list(np.arange(-70, 0))) #last 30min

    def __init__(self, ds, **params):
        super().__init__(**params)
        self._ds = ds
        # Refresh data using a button
        self.refresh = pn.widgets.Button(name='Refresh Data', button_type='primary')
        def update_data(event):
            today = date.today().strftime("%Y%m%d")
            files = sorted(glob.glob(f'/data/{today}.nc')) # 500 second
            self._ds = xr.open_mfdataset(files)
            print(f'File list refreshed {files[-5:]}')
        self.refresh.on_click(update_data)
        self.base_map() # create basemap

    def base_map(self):
        tiles = gv.tile_sources.StamenTonerBackground().apply.opts()
        # site
        ntc = gv.Text(-73.82, 40.7, 'National Tennis Center').opts(text_font_style='bold')
        site = gv.Points([(-73.8492017,40.7498823)]).opts(color='red', line_color='black', line_width=0.5, size=5)
        site_shapely = Point(-73.8492017,40.7498823)
        first_circle = gv.Shape(site_shapely.buffer(0.0573)).opts(line_color='red', line_dash='dashed', fill_alpha=0,line_width=2, alpha=0.5) #3mi
        second_circle = gv.Shape(site_shapely.buffer(0.0955)).opts(line_color='blue', line_dash='dashed',  fill_alpha=0, line_width=2, alpha=0.5) #5mi
        third_circle = gv.Shape(site_shapely.buffer(0.191)).opts(line_color='green', line_dash='dashed',  fill_alpha=0, line_width=2, alpha=0.5) #10mi
        cirle_anno1 = gv.Text(-73.8492017, 40.807, '3mi').opts(text_alpha=0.6, text_font_size='12px')
        cirle_anno2 = gv.Text(-73.8492017, 40.846, '5mi').opts(text_alpha=0.6, text_font_size='12px')
        cirle_anno3 = gv.Text(-73.8492017, 40.941, '10mi').opts(text_alpha=0.6, text_font_size='12px')
        out = tiles * site * ntc * first_circle * second_circle * third_circle * cirle_anno1 * cirle_anno2 * cirle_anno3
        self._base = out

    @param.depends('var_selected', 'scan_angle', 'timestep')
    def make_map(self):
        ### TODO how to handle the dependency
        ### https://panel.holoviz.org/user_guide/Param.html
        timestamp = self._ds['time'][self.timestep].astype(int)
        ns = 1e-9
        dt = datetime.utcfromtimestamp(timestamp * ns)
        ts = dt.strftime("%Y/%m/%d %H:%M:%S")
        
        # Need to solve this part
        data = self._ds[self.var_selected].sel(scan_angle=self.scan_angle).isel(time=self.timestep).load() 
        NWSVelocity = [
            '#90009F',
            '#00FF00',
            '#00E800',
            '#00C800',
            '#00B000',
            '#009000',
            '#007000',
            '#779777',
            '#977777',
            '#800000',
            '#A00000',
            '#B80000',
            '#D80000',
            '#EE0000',
            '#FF0000']
        NWSReflectivity = [
            '#00ECEC',
            '#01A0F6',
            '#0000F6',
            '#00FF00',
            '#00C800',
            '#009000',
            '#FFFF00',
            '#E7C000',
            '#FF9000',
            '#FF0000',
            '#D60000',
            '#C00000',
            '#FF00FF',
            '#9955C9',
            '#000000']
        if self.var_selected in ['Reflectivity', 'CorrectedReflectivity','DifferentialReflectivity', 'CorrectedDifferentialReflectivity']:
            color_map = NWSReflectivity
        elif self.var_selected in ['Velocity']:
            color_map = NWSVelocity
        else:
            color_map = 'gist_ncar'
        data = hv.Dataset(data, vdims=self.var_selected)
        graph = data.to(gv.Image, ["lon", "lat"])
        graph = (
            rasterize(graph).opts(title=ts, height=550, width=800, colorbar=True, cnorm='eq_hist', cmap=color_map, alpha=0.5, tools=['hover'])
        ) 
        return graph * self._base


skyler = SkylerApp(ds)
## Create Dashboard from template
dark_material = pn.template.MaterialTemplate(title='US Open Skyler', theme='dark')
# dark_material.sidebar.append(skyler.var_selected)
# dark_material.sidebar.append(skyler.angle)
dark_material.sidebar.append(skyler.param['var_selected'])
dark_material.sidebar.append(skyler.param['scan_angle'])
dark_material.sidebar.append(skyler.refresh)
dark_material.main.append(
    pn.Row(
        pn.Card(skyler.make_map)
    )
)
dark_material.main.append(
    pn.Row(pn.widgets.DiscretePlayer.from_param(skyler.param['timestep'], loop_policy='loop', interval=2000))
)
dark_material.servable()
1 Like