Skip to content

Instantly share code, notes, and snippets.

@epifanio
Created October 18, 2024 00:14
Show Gist options
  • Save epifanio/4bf5368b77c65b07ec112556c2f93a86 to your computer and use it in GitHub Desktop.
Save epifanio/4bf5368b77c65b07ec112556c2f93a86 to your computer and use it in GitHub Desktop.
import hvplot.xarray
import xarray as xr
from bokeh.layouts import Spacer
from bokeh.models.widgets import Dropdown
from bokeh.layouts import column, row, Spacer
from bokeh.io import curdoc
from urllib.parse import unquote
import holoviews as hv
import gc
ds = None
renderer = hv.renderer('bokeh').instance(mode='server')
def load_data(url):
try:
del ds
gc.collect()
except UnboundLocalError:
pass
ds = None
try:
# attempt to load the dataset via xarray with decode_times=True
ds = xr.open_dataset(str(url).strip())
except ValueError as e:
# attempt to load the dataset via xarray with decode_times=False
ds = xr.open_dataset(str(url).strip(), decode_times=False)
print(e)
except OSError as e:
print(e)
if ds and not ds.coords:
del ds
gc.collect()
# the following hack is used when the dataset is served as a tabledap via erdap
erdapp_uglyness = list(dict(xr.open_dataset(url).dims).keys())[0]
renamed_vars = {i:i.replace(erdapp_uglyness+".", "") for i in list(xr.open_dataset(url).variables.keys())}
new_nc_url = url+'?'+'time,'+','.join(list(xr.open_dataset(url).variables)).replace(f"{erdapp_uglyness}.", "").replace(f"time,", "")
ds = xr.open_dataset(new_nc_url)
ds = ds.set_coords(f"{erdapp_uglyness}.time")
ds = ds.swap_dims(s=f"time")
ds = ds.set_xindex(f"{erdapp_uglyness}.time")
ds = ds.rename_vars(renamed_vars)
return ds
def plot(var, title=None):
try:
del plot_widget
gc.collect()
except UnboundLocalError:
pass
plot_widget = None
try:
del ds
gc.collect()
except UnboundLocalError:
pass
gc.collect()
ds = load_data(url)
print(f'plotting var: {var}')
if not title:
try:
title = f"{ds[var].attrs['long_name']}"
except KeyError:
title = f"{var}"
else:
title=title
if 'featureType' in ds.attrs:
featureType = ds.attrs['featureType'].lower()
elif 'cdm_data_type' in ds[var].attrs:
featureType = ds[var].attrs['cdm_data_type'].lower()
else:
featureType = None
is_monotonic = False
if featureType == 'timeseries':
axis_arguments = {'grid':True, 'title': title, 'responsive': True}
plot_widget = ds[var].hvplot.line(**axis_arguments)
return plot_widget
if featureType != "timeseries":
axis_arguments = {'x': ds[var], 'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': True}
try:
plot_widget = ds[var].hvplot.line(**axis_arguments)
except TypeError:
axis_arguments = {'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': True}
plot_widget = ds[var].hvplot.line(**axis_arguments)
except ValueError:
axis_arguments = {'x': var, 'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': True}
plot_widget = ds[var].hvplot.line(**axis_arguments)
return plot_widget
def on_var_select(var):
return plot(var=var, title=var)
def safe_check(var):
try:
ds[var].values
return var
except Exception as e:
# Handle the exception (e.g., log it, return False, etc.)
print(f"Error processing {var}: {e}")
return False
args = curdoc().session_context.request.arguments
url = unquote(args.get('url')[0].decode("utf-8"))
print("++++++++++++++++++++++++ LOADING ++++++++++++++++++++++++++++++++++++")
print(str(url))
print("++++++++++++++++++++++++ +++++++ ++++++++++++++++++++++++++++++++++++")
ds = load_data(url)
if ds:
plottable_vars = [j for j in ds if len([value for value in list(ds[j].coords) if value in list(ds.dims)]) >= 1]
plottable_vars = [i for i in plottable_vars if safe_check(i)]
print("plottable_vars:", plottable_vars )
dmap = hv.DynamicMap(on_var_select, kdims=['var'])
dmap = dmap.redim.values(var=plottable_vars)
plot_container = [renderer.get_plot(dmap)]
plot_container = column(row(plot_container))
curdoc().add_root(plot_container)
@epifanio
Copy link
Author

new version

import hvplot.xarray
import xarray as xr
import holoviews as hv
from bokeh.layouts import column
from bokeh.models import Select
from bokeh.io import curdoc
import gc
# hv.extension('bokeh')

 
def safe_check(var):
    try:
        ds[var].values
        return var
    except Exception as e:
        # Handle the exception (e.g., log it, return False, etc.)
        print(f"Error processing {var}: {e}")
        return False

                   
def load_data(url):
    try:
        del ds
        gc.collect()
    except UnboundLocalError:
        pass
    ds = None
    try:
        # attempt to load the dataset via xarray with decode_times=True
        ds = xr.open_dataset(str(url).strip())
    except ValueError as e:
        # attempt to load the dataset via xarray with decode_times=False
        ds = xr.open_dataset(str(url).strip(), decode_times=False)
        print(e)
    except OSError as e:
        print(e)
    if ds and not ds.coords:
        del ds
        gc.collect()
        # the following hack is used when the dataset is served as a tabledap via erdap
        erdapp_uglyness = list(dict(xr.open_dataset(url).dims).keys())[0]
        renamed_vars = {i:i.replace(erdapp_uglyness+".", "") for i in list(xr.open_dataset(url).variables.keys())}
        new_nc_url = url+'?'+'time,'+','.join(list(xr.open_dataset(url).variables)).replace(f"{erdapp_uglyness}.", "").replace(f"time,", "")
        ds = xr.open_dataset(new_nc_url)
        ds = ds.set_coords(f"{erdapp_uglyness}.time")
        ds = ds.swap_dims(s=f"time")
        ds = ds.set_xindex(f"{erdapp_uglyness}.time")
        ds = ds.rename_vars(renamed_vars)
    return ds


def update_plot(attr, old, new, var=None, title=None):
    print('update plot')
    if var is None:
        var = new
        title = new
    if 'featureType' in ds.attrs:
        featureType = ds.attrs['featureType'].lower()
    elif 'cdm_data_type' in ds[var].attrs:
        featureType = ds[var].attrs['cdm_data_type'].lower()
    else:
        featureType = None
    if featureType == 'timeseries':
        axis_arguments = {'grid':True, 'title': title, 'responsive': False}
        plot_widget =  ds[var].where(ds[var] != 9.96921e36).hvplot.line(**axis_arguments)
        #return plot_widget
    if featureType != "timeseries":
        axis_arguments = {'x': ds[var], 'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': False}
        try:
            plot_widget =  ds[var].where(ds[var] != 9.96921e36).hvplot.line(**axis_arguments)
        except TypeError:
            axis_arguments = {'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': False}
            plot_widget =  ds[var].where(ds[var] != 9.96921e36).hvplot.line(**axis_arguments)
        except ValueError:
            axis_arguments = {'x': var, 'grid':True, 'title': title, 'widget_location': 'bottom', 'responsive': False}
            plot_widget =  ds[var].where(ds[var] != 9.96921e36).hvplot.line(**axis_arguments)
        #return plot_widget
    try:
        container.children[1] = hv.render(plot_widget)
    except NameError:
        return plot_widget
    
ds = load_data(curdoc().session_context.request.arguments['url'][0].decode('utf-8'))

if ds:
    plottable_vars = [j for j in ds if len([value for value in list(ds[j].coords) if value in list(ds.dims)]) >= 1]
    plottable_vars = [i for i in plottable_vars if safe_check(i)]
    print("plottable_vars:", plottable_vars )
    mapping_var_names = {}
    for i in plottable_vars:
        if int(len(list(ds[i].coords)) != 0):
            try:
                title = f"{ds[i].attrs['long_name']} [{i}]"
            except KeyError:
                title = f"{i}"
            mapping_var_names[i] = title

select = Select(title="Select a variable:", value=plottable_vars[0], options=plottable_vars)
select.on_change('value', update_plot)
container = column(select, hv.render(update_plot(attr=None, old=None, new=None, var=select.value, title=select.value)))

curdoc().add_root(container)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment