In [ ]:
import iris
import param
import numpy as np
import holoviews as hv
import holocube as hc

from cartopy import feature as cf
from paramnb import NbParams, FileSelector

hv.notebook_extension(width=90)
%output widgets='live' size=400
%opts Image {+framewise} [colorbar=True] Contours {+framewise}

Declaring a set of widgets using paramNB is as simply as declaring a Parameterized class. Here we declare a CubeLoader class with a FileSelector parameter, a cache and a load method, which will load cubes from a file on disk. The NbParams function accepts the CubeLoader instance and generates a dropdown widget for the cube_path from it. By declaring a callback we can tell it to load the file from disk (or the cache) whenever the user executes the widget. Finally we can declare the execute mode, which allows declaring no execution with execute=None or adds a button to execute the 'next' cell or all cells 'below'.


In [ ]:
class CubeLoader(param.Parameterized):
    
    cube_path = FileSelector(default='files/*.pp')

    cache = {}
    cubes = None
    
    @classmethod
    def load(cls, cube_loader):
        if cls.cube_path not in cls.cache:
            cubelist = iris.load(cls.cube_path)
            for c in cubelist:
                c.coord('grid_longitude').guess_bounds()
                c.coord('grid_latitude').guess_bounds()
            cls.cache[cls.cube_path] = cubelist
        else:
            cubelist = cls.cache[cls.cube_path]
        cubes = {cb.vdims[0].name:cb for cb in [hc.HoloCube(c) for c in cubelist]} # Load cubes into dictionary
        cls.cubes = {k:v for k,v in cubes.items() if k!='unknown'}  # Filter as desired

NbParams(CubeLoader, execute='next', callback=CubeLoader.load)

In [ ]:
from cartopy import crs as ccrs
from matplotlib.cm import cmap_d

projections = {k: crs for k, crs in param.concrete_descendents(ccrs.CRS).items()
               if hasattr(crs, '_as_mpl_axes') and not k[0] == '_'}

class CubeBrowser(param.Parameterized):
    """
    CubeBrowser defines a small example GUI to demonstrate
    how to define a small set of widgets to control plotting
    of an iris Cube. It exposes control over the colormap,
    the quantity to be plotted, the element to plot it with
    and the projection to plot it on.
    """

    cmap = param.ObjectSelector(default='viridis',
                                objects=list(cmap_d.keys()))

    quantity = param.ObjectSelector(default=CubeLoader.cubes.keys()[0],
                                    objects=list(CubeLoader.cubes.keys()))

    element = param.ObjectSelector(default=hc.Image,
                                   objects=[hc.Image, hc.Contours])

    projection = param.ObjectSelector(default='default',
                                      objects=['default']+sorted(projections.keys()))
    
    cache = {}

    @classmethod
    def view(cls):
        key = (cls.quantity, cls.element)
        if key in CubeBrowser.cache:
            converted = cls.cache[key]
        else:
            holocube = CubeLoader.cubes[cls.quantity]
            converted = holocube.to(cls.element, ['grid_longitude', 'grid_latitude'], dynamic=True)
            cls.cache[key] = converted
        styled = converted(style={cls.element.name: dict(cmap=cls.cmap)})
        projection = projections[cls.projection]() if cls.projection != 'default' else None
        projected = styled({'Image': dict(plot=dict(projection=projection))}) if projection else styled
        return (projected * hc.GeoFeature(cf.COASTLINE)(plot=dict(scale='50m')))

NbParams(CubeBrowser, execute='next')

In [ ]:
# Finally we can declare a cell which uses the settings defined via the widgets to render the requested plot.
# We simply look up the correct cube, convert it to the desired Element type and then display it with the requested options.
CubeBrowser.view()