Source code for eva.plotting.emcpy.plot_tools.figure_driver
# (C) Copyright 2021-2022 NOAA/NWS/EMC
#
# (C) Copyright 2021-2022 United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
# --------------------------------------------------------------------------------------------------
from eva.eva_path import return_eva_path
from eva.utilities.stats import stats_helper
from eva.utilities.utils import get_schema, camelcase_to_underscore, parse_channel_list
from eva.utilities.utils import replace_vars_dict
from emcpy.plots.create_plots import CreatePlot, CreateFigure
import copy
import importlib as im
import os
# --------------------------------------------------------------------------------------------------
[docs]def figure_driver(config, data_collections, timing, logger):
"""
Generates and saves multiple figures based on the provided configuration.
Args:
config (dict): A dictionary containing the configuration for generating figures.
data_collections (DataCollections): An instance of the DataCollections class containing
input data.
timing (Timing): A timing instance to measure the execution time.
logger (Logger): An instance of the logger for logging messages.
This function generates and saves multiple figures based on the provided configuration. It
processes each graphic specified in the configuration and creates corresponding figures with
plots.
"""
# Get list of graphics from configuration
# -------------------
graphics = config.get("graphics")
# Loop through specified graphics
# -------------------
timing.start('Graphics Loop')
for graphic in graphics:
# Parse configuration for this graphic
# -------------------
batch_conf = graphic.get("batch figure", {}) # batch configuration (default nothing)
figure_conf = graphic.get("figure") # figure configuration
plots_conf = graphic.get("plots") # list of plots/subplots
dynamic_options_conf = graphic.get("dynamic options", []) # Dynamic overwrites
# update figure conf based on schema
# ----------------------------------
fig_schema = figure_conf.get('schema', os.path.join(return_eva_path(), 'plotting',
'emcpy', 'defaults', 'figure.yaml'))
figure_conf = get_schema(fig_schema, figure_conf, logger)
# pass configurations and make graphic(s)
# ---------------------------------------
if batch_conf:
# Get potential variables
variables = batch_conf.get('variables', [])
# Get list of channels
channels_str_or_list = batch_conf.get('channels', [])
channels = parse_channel_list(channels_str_or_list, logger)
# Set some fake values to ensure the loops are entered
if variables == []:
logger.abort("Batch Figure must provide variables, even if with channels")
if channels == []:
channels = ['none']
# Loop over variables and channels
for variable in variables:
for channel in channels:
batch_conf_this = {}
batch_conf_this['variable'] = variable
# Version to be used in titles
batch_conf_this['variable_title'] = variable.replace('_', ' ').title()
channel_str = str(channel)
if channel_str != 'none':
batch_conf_this['channel'] = channel_str
var_title = batch_conf_this['variable_title'] + ' Ch. ' + channel_str
batch_conf_this['variable_title'] = var_title
# Replace templated variables in figure and plots config
figure_conf_fill = copy.copy(figure_conf)
figure_conf_fill = replace_vars_dict(figure_conf_fill, **batch_conf_this)
plots_conf_fill = copy.copy(plots_conf)
plots_conf_fill = replace_vars_dict(plots_conf_fill, **batch_conf_this)
dynamic_options_conf_fill = copy.copy(dynamic_options_conf)
dynamic_options_conf_fill = replace_vars_dict(dynamic_options_conf_fill,
**batch_conf_this)
# Make plot
make_figure(figure_conf_fill, plots_conf_fill,
dynamic_options_conf_fill, data_collections, logger)
else:
# make just one figure per configuration
make_figure(figure_conf, plots_conf, dynamic_options_conf, data_collections, logger)
timing.stop('Graphics Loop')
# --------------------------------------------------------------------------------------------------
[docs]def make_figure(figure_conf, plots, dynamic_options, data_collections, logger):
"""
Generates a figure based on the provided configuration and plots.
Args:
figure_conf (dict): A dictionary containing the configuration for the figure layout
and appearance.
plots (list): A list of dictionaries containing plot configurations.
dynamic_options (list): A list of dictionaries containing dynamic configuration options.
data_collections (DataCollections): An instance of the DataCollections class containing
input data.
logger (Logger): An instance of the logger for logging messages.
This function generates a figure based on the provided configuration and plot settings. It
processes the specified plots, applies dynamic options, and saves the generated figure.
"""
# Adjust the plots configs if there are dynamic options
# -----------------------------------------------------
for dynamic_option in dynamic_options:
dynamic_option_module = im.import_module("eva.plotting.emcpy.plot_tools.dynamic_config")
dynamic_option_method = getattr(dynamic_option_module, dynamic_option['type'])
plots = dynamic_option_method(logger, dynamic_option, plots, data_collections)
# Grab some figure configuration
# -------------------
figure_layout = figure_conf.get("layout")
file_type = figure_conf.get("figure file type", "png")
output_file = get_output_file(figure_conf)
# Set up layers and plots
plot_list = []
for plot in plots:
layer_list = []
for layer in plot.get("layers"):
eva_class_name = layer.get("type")
eva_module_name = camelcase_to_underscore(eva_class_name)
full_module = "eva.plotting.emcpy.diagnostics."+eva_module_name
layer_class = getattr(im.import_module(full_module), eva_class_name)
# use the translator class to go from eva to declarative plotting
layer_list.append(layer_class(layer, logger, data_collections).plotobj)
# get mapping dictionary
proj = None
domain = None
if 'mapping' in plot.keys():
mapoptions = plot.get('mapping')
# TODO make this configurable and not hard coded
proj = mapoptions['projection']
domain = mapoptions['domain']
# create a subplot based on specified layers
plotobj = CreatePlot(plot_layers=layer_list, projection=proj, domain=domain)
# make changes to subplot based on YAML configuration
for key, value in plot.items():
if key not in ['layers', 'mapping', 'statistics']:
if isinstance(value, dict):
getattr(plotobj, key)(**value)
elif value is None:
getattr(plotobj, key)()
else:
getattr(plotobj, key)(value)
if key in ['statistics']:
# call the stats helper
stats_helper(logger, plotobj, data_collections, value)
plot_list.append(plotobj)
# create figure
fig = CreateFigure(nrows=figure_conf['layout'][0],
ncols=figure_conf['layout'][1],
figsize=tuple(figure_conf['figure size']))
fig.plot_list = plot_list
fig.create_figure()
if 'title' in figure_conf:
fig.add_suptitle(figure_conf['title'])
if 'tight layout' in figure_conf:
if isinstance(figure_conf['tight layout'], dict):
fig.tight_layout(**figure_conf['tight layout'])
else:
fig.tight_layout()
if 'plot logo' in figure_conf:
fig.plot_logo(**figure_conf['plot logo'])
saveargs = get_saveargs(figure_conf)
fig.save_figure(output_file, **saveargs)
fig.close_figure()
# --------------------------------------------------------------------------------------------------
[docs]def get_saveargs(figure_conf):
"""
Gets arguments for saving a figure based on the provided configuration.
Args:
figure_conf (dict): A dictionary containing the figure configuration.
Returns:
out_conf (dict): A dictionary containing arguments for saving the figure.
This function extracts relevant arguments from the provided figure configuration to be used
for saving the generated figure.
Example:
::
figure_conf = {
"layout": [2, 2],
"figure file type": "png",
"output path": "./output_folder",
"output name": "example_figure"
}
save_args = get_saveargs(figure_conf)
"""
out_conf = figure_conf
delvars = ['layout', 'figure file type', 'output path', 'figure size', 'title']
out_conf['format'] = figure_conf['figure file type']
for d in delvars:
del out_conf[d]
return out_conf
# --------------------------------------------------------------------------------------------------
[docs]def get_output_file(figure_conf):
"""
Gets the output file path for saving the figure.
Args:
figure_conf (dict): A dictionary containing the figure configuration.
Returns:
output_file (str): The complete path for saving the figure.
This function constructs the complete file path for saving the generated figure based on the
provided figure configuration.
Example:
::
figure_conf = {
"output path": "./output_folder",
"output name": "example_figure"
}
output_file = get_output_file(figure_conf)
"""
file_path = figure_conf.get("output path", "./")
output_name = figure_conf.get("output name", "")
output_file = os.path.join(file_path, output_name)
return output_file
# --------------------------------------------------------------------------------------------------