Skip to content

Commit

Permalink
Adding the hvplot backend (JCSDA-internal#186)
Browse files Browse the repository at this point in the history
## Description

This PR expands upon the hvplot backend introduced in JCSDA-internal#158. All other
diagnostic types are reformatted to have a base class and child classes
for each backend. Each base class includes two methods: `data_prep` and
`configure_plot`. `configure_plot` is a virtual method that is resolved
once a child class for either emcpy or hvplot is instantiated.

`figure_driver` is updated to reflect these changes
  • Loading branch information
asewnath authored May 20, 2024
1 parent eb709c1 commit 5cf9a62
Show file tree
Hide file tree
Showing 35 changed files with 896 additions and 952 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ hvplot
nbconvert
bokeh
geopandas
geoviews
59 changes: 29 additions & 30 deletions src/eva/plotting/batch/base/diagnostics/density.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
from eva.eva_path import return_eva_path
from eva.utilities.config import get
from eva.utilities.utils import get_schema, update_object, slice_var_from_str
import emcpy.plots.plots
import os
import numpy as np

from abc import ABC, abstractmethod

# --------------------------------------------------------------------------------------------------


class Density():
class Density(ABC):

"""Base class for creating density plots."""

def __init__(self, config, logger, dataobj):

"""
Creates a density plot based on the provided configuration and data.
Creates a density plot abstract class based on the provided configuration and data.
Args:
config (dict): A dictionary containing the configuration for the density plot.
logger (Logger): An instance of the logger for logging messages.
dataobj: An instance of the data object containing input data.
This class initializes and configures a density plot based on the provided configuration and
data. The density plot is created using a declarative plotting library from EMCPy
(https://github.com/NOAA-EMC/emcpy).
Example:
::
Expand All @@ -46,45 +41,49 @@ def __init__(self, config, logger, dataobj):
density_plot = Density(config, logger, dataobj)
"""

self.config = config
self.logger = logger
self.dataobj = dataobj
self.plotobj = None
self.data = None

# --------------------------------------------------------------------------------------------------

def data_prep(self):

""" Preparing data for configure_plot """

# Get the data to plot from the data_collection
# ---------------------------------------------
varstr = config['data']['variable']
varstr = self.config['data']['variable']
var_cgv = varstr.split('::')

if len(var_cgv) != 3:
logger.abort('In Density the variable \'var_cgv\' does not appear to ' +
'be in the required format of collection::group::variable.')
self.logger.abort('In Density the variable \'var_cgv\' does not appear to ' +
'be in the required format of collection::group::variable.')

# Optionally get the channel to plot
channel = None
if 'channel' in config['data']:
channel = config['data'].get('channel')
if 'channel' in self.config['data']:
channel = self.config['data'].get('channel')

data = dataobj.get_variable_data(var_cgv[0], var_cgv[1], var_cgv[2], channel)
data = self.dataobj.get_variable_data(var_cgv[0], var_cgv[1], var_cgv[2], channel)

# See if we need to slice data
data = slice_var_from_str(config['data'], data, logger)
data = slice_var_from_str(self.config['data'], data, self.logger)

# Density data should be flattened
data = data.flatten()

# Missing data should also be removed
mask = ~np.isnan(data)
data = data[mask]

# Create declarative plotting density object
# --------------------------------------------
self.plotobj = emcpy.plots.plots.Density(data)

# Get defaults from schema
# ------------------------
layer_schema = config.get('schema', os.path.join(return_eva_path(), 'plotting',
'emcpy', 'defaults', 'density.yaml'))
config = get_schema(layer_schema, config, logger)
delvars = ['type', 'schema', 'data']
for d in delvars:
config.pop(d, None)
self.plotobj = update_object(self.plotobj, config, logger)
self.data = data[mask]

# --------------------------------------------------------------------------------------------------

@abstractmethod
def configure_plot(self):
""" Virtual method for configuring plot based on selected backend """
pass

# --------------------------------------------------------------------------------------------------
53 changes: 26 additions & 27 deletions src/eva/plotting/batch/base/diagnostics/histogram.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
from eva.eva_path import return_eva_path
from eva.utilities.config import get
from eva.utilities.utils import get_schema, update_object, slice_var_from_str
import emcpy.plots.plots
import os
import numpy as np

from abc import ABC, abstractmethod

# --------------------------------------------------------------------------------------------------


class Histogram():
class Histogram(ABC):

"""Base class for creating histogram plots."""

def __init__(self, config, logger, dataobj):

"""
Creates a histogram plot based on the provided configuration and data.
Creates a histogram plot abstract class based on the provided configuration and data.
Args:
config (dict): A dictionary containing the configuration for the histogram plot.
logger (Logger): An instance of the logger for logging messages.
dataobj: An instance of the data object containing input data.
This class initializes and configures a histogram plot based on the provided configuration
and data. The histogram plot is created using a declarative plotting library from EMCPy
(https://github.com/NOAA-EMC/emcpy).
Example:
Expand All @@ -46,9 +42,20 @@ def __init__(self, config, logger, dataobj):
histogram_plot = Histogram(config, logger, dataobj)
"""

self.config = config
self.logger = logger
self.dataobj = dataobj
self.data = None
self.plotobj = None

# --------------------------------------------------------------------------------------------------

def data_prep(self):
""" Preparing data for configure_plot """

# Get the data to plot from the data_collection
# ---------------------------------------------
varstr = config['data']['variable']
varstr = self.config['data']['variable']
var_cgv = varstr.split('::')

if len(var_cgv) != 3:
Expand All @@ -57,34 +64,26 @@ def __init__(self, config, logger, dataobj):

# Optionally get the channel to plot
channel = None
if 'channel' in config['data']:
channel = config['data'].get('channel')
if 'channel' in self.config['data']:
channel = self.config['data'].get('channel')

data = dataobj.get_variable_data(var_cgv[0], var_cgv[1], var_cgv[2], channel)
data = self.dataobj.get_variable_data(var_cgv[0], var_cgv[1], var_cgv[2], channel)

# See if we need to slice data
data = slice_var_from_str(config['data'], data, logger)
data = slice_var_from_str(self.config['data'], data, self.logger)

# Histogram data should be flattened
data = data.flatten()

# Missing data should also be removed
mask = ~np.isnan(data)
data = data[mask]

# Create declarative plotting histogram object
# --------------------------------------------
self.plotobj = emcpy.plots.plots.Histogram(data)

# Get defaults from schema
# ------------------------
layer_schema = config.get('schema', os.path.join(return_eva_path(), 'plotting',
'emcpy', 'defaults', 'histogram.yaml'))
config = get_schema(layer_schema, config, logger)
delvars = ['type', 'schema', 'data']
for d in delvars:
config.pop(d, None)
self.plotobj = update_object(self.plotobj, config, logger)
self.data = data[mask]

# --------------------------------------------------------------------------------------------------

@abstractmethod
def configure_plot(self):
""" Virtual method for configuring plot based on selected backend """
pass

# --------------------------------------------------------------------------------------------------
41 changes: 17 additions & 24 deletions src/eva/plotting/batch/base/diagnostics/horizontal_line.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from eva.eva_path import return_eva_path
from eva.utilities.utils import get_schema, update_object
import emcpy.plots.plots
import os


from abc import ABC, abstractmethod
# --------------------------------------------------------------------------------------------------


Expand All @@ -14,15 +12,12 @@ class HorizontalLine():
def __init__(self, config, logger, dataobj):

"""
Creates a horizontal line plot based on the provided configuration.
Creates a horizontal line plot abstract class based on the provided configuration.
Args:
config (dict): A dictionary containing the configuration for the horizontal line plot.
logger (Logger): An instance of the logger for logging messages.
This class initializes and configures a horizontal line plot based on the provided
configuration. The horizontal line plot is created using a declarative plotting library from
EMCPy (https://github.com/NOAA-EMC/emcpy).
dataobj: An instance of the data object containing input data.
Example:
Expand All @@ -38,24 +33,22 @@ def __init__(self, config, logger, dataobj):
horizontal_line_plot = HorizontalLine(config, logger)
"""

# Get the y value to plot
# -----------------------
yval = config['y']
self.logger = logger
self.config = config
self.yval = None

# Create declarative plotting HorizontalLine object
# -------------------------------------------
self.plotobj = emcpy.plots.plots.HorizontalLine(yval)
# --------------------------------------------------------------------------------------------------

# Get defaults from schema
# ------------------------
layer_schema = config.get('schema', os.path.join(return_eva_path(), 'plotting',
'emcpy', 'defaults',
'horizontal_line.yaml'))
config = get_schema(layer_schema, config, logger)
delvars = ['type', 'schema']
for d in delvars:
config.pop(d, None)
self.plotobj = update_object(self.plotobj, config, logger)
def data_prep(self):
""" Preparing data for configure_plot """

# Get the y value to plot
# -----------------------
self.yval = self.config['y']

# --------------------------------------------------------------------------------------------------

@abstractmethod
def configure_plot(self):
""" Virtual method for configuring plot based on selected backend """
pass
Loading

0 comments on commit 5cf9a62

Please sign in to comment.