Source code for psy_simple.plugin

"""psy-simple psyplot plugin

This module defines the rcParams for the psy-simple plugin.
"""

# Disclaimer
# ----------
#
# Copyright (C) 2021 Helmholtz-Zentrum Hereon
# Copyright (C) 2020-2021 Helmholtz-Zentrum Geesthacht
# Copyright (C) 2016-2021 University of Lausanne
#
# This file is part of psy-simple and is released under the GNU LGPL-3.O license.
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3.0 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LGPL-3.0 license for more details.
#
# You should have received a copy of the GNU LGPL-3.0 license
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import six
import re
import matplotlib as mpl
import dataclasses
import numpy as np
import enum
from matplotlib.patches import ArrowStyle
from warnings import warn
from itertools import repeat
from psyplot.config.rcsetup import (
    RcParams, safe_list, SubDict, validate_dict, validate_stringlist,
    validate_stringset)
from matplotlib.rcsetup import (
    validate_bool, validate_color, validate_fontsize,
    ValidateInStrings, validate_int, validate_colorlist
)
from psy_simple import __version__ as plugin_version
import xarray as xr


[docs]def get_versions(requirements=True): return {'version': plugin_version}
[docs]def patch_prior_1_0(plotter_d, versions): """Patch psy_simple plotters for versions smaller than 1.0 Before psyplot 1.0.0, the plotters in the psy_simple package where part of the psyplot.plotter.simple module. This has to be corrected""" plotter_d['cls'] = ('psy_simple.plotters', plotter_d['cls'][1])
#: patches to apply when loading a project patches = { ('psyplot.plotter.simple', 'LinRegPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'DensityRegPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'ViolinPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'Simple2DPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'SimpleVectorPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'BarPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'CombinedSimplePlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'DensityPlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'LinePlotter'): patch_prior_1_0, ('psyplot.plotter.simple', 'SimplePlotterBase'): patch_prior_1_0, } bound_strings = ['data', 'mid', 'rounded', 'roundedsym', 'minmax', 'sym', 'log', 'symlog'] tick_strings = bound_strings + ['hour', 'day', 'week', 'month', 'monthend', 'monthbegin', 'year', 'yearend', 'yearbegin']
[docs]class strEnum(str, enum.Enum): pass
BoundsMethod = strEnum( 'BoundsMethod', zip(bound_strings, bound_strings), module=__name__) cticks_strings = bound_strings + ['bounds', 'midbounds'] CTicksMethod = strEnum( 'CTicksMethod', zip(cticks_strings, cticks_strings), module=__name__) TicksMethod = strEnum( 'TicksMethod', zip(tick_strings, tick_strings), module=__name__)
[docs]@dataclasses.dataclass class BoundsType: method: BoundsMethod N: int = None percmin: float = 0 percmax: float = 100 vmin: float = None vmax: float = None def __post_init__(self): for field, val in zip(dataclasses.fields(self), self): if val is not None or field.name == 'method': val = field.type(val) setattr(self, field.name, val) def __iter__(self): return iter(dataclasses.astuple(self))
[docs]@dataclasses.dataclass class CTicksType(BoundsType): method: CTicksMethod
[docs]@dataclasses.dataclass class TicksType(BoundsType): method: TicksMethod
[docs]def try_and_error(*funcs): """Apply multiple validation functions Parameters ---------- ``*funcs`` Validation functions to test Returns ------- function""" def validate(value): exc = None for func in funcs: try: return func(value) except (ValueError, TypeError) as e: exc = e raise exc return validate
# ----------------------------------------------------------------------------- # ------------------------- validation functions ------------------------------ # -----------------------------------------------------------------------------
[docs]def validate_str(s): """Validate a string Parameters ---------- s: str Returns ------- str Raises ------ ValueError""" if not isinstance(s, six.string_types): raise ValueError("Did not found string!") return six.text_type(s)
[docs]def validate_float(s): """convert `s` to float or raise Returns ------- s converted to a float: float Raises ------ ValueError""" try: return float(s) except (ValueError, TypeError): raise ValueError('Could not convert "%s" to float' % str(s))
[docs]def validate_text(value): """Validate a text formatoption Parameters ---------- value: see :attr:`psyplot.plotter.labelplotter.text` Raises ------ ValueError""" possible_transform = ['axes', 'fig', 'data'] validate_transform = ValidateInStrings('transform', possible_transform, True) tests = [validate_float, validate_float, validate_str, validate_transform, dict] if isinstance(value, six.string_types): xpos, ypos = rcParams['texts.default_position'] return [(xpos, ypos, value, 'axes', {'ha': 'right'})] elif isinstance(value, tuple): value = [value] try: value = list(value)[:] except TypeError: raise ValueError("Value must be string or list of tuples!") for i, val in enumerate(value): try: val = tuple(val) except TypeError: raise ValueError( "Text must be an iterable of the form " "(x, y, s[, trans, params])!") if len(val) < 3: raise ValueError( "Text tuple must at least be like [x, y, s], with floats x, " "y and string s!") elif len(val) == 3 or isinstance(val[3], dict): val = list(val) val.insert(3, 'data') if len(val) == 4: val += [{}] val = tuple(val) if len(val) > 5: raise ValueError( "Text tuple must not be longer then length 5. It can be " "like (x, y, s[, trans, params])!") value[i] = (validate(x) for validate, x in zip(tests, val)) return value
[docs]def validate_fontweight(value): if value is None: return None elif isinstance(value, six.string_types): return six.text_type(value) elif mpl.__version__ >= '1.5': return validate_float(value) raise ValueError("Font weights must be None or a string!")
[docs]def validate_limits(value): if value is None or isinstance(value, six.string_types): return (value, value) if not len(value) == 2: raise ValueError("Limits must have length 2!") return tuple(value)
[docs]def validate_none(b): """Validate that None is given Parameters ---------- b: {None, 'none'} None or string (the case is ignored) Returns ------- None Raises ------ ValueError""" if isinstance(b, six.string_types): b = b.lower() if b is None or b == 'none': return None else: raise ValueError('Could not convert "%s" to None' % b)
validate_bool_maybe_none = try_and_error(validate_none, validate_bool)
[docs]def validate_axiscolor(value): """Validate a dictionary containing axiscolor definitions Parameters ---------- value: dict see :attr:`psyplot.plotter.baseplotter.axiscolor` Returns ------- dict Raises ------ ValueError""" validate = try_and_error(validate_none, validate_color) possible_keys = {'right', 'left', 'top', 'bottom'} try: value = dict(value) false_keys = set(value) - possible_keys if false_keys: raise ValueError("Wrong keys (%s)!" % (', '.join(false_keys))) for key, val in value.items(): value[key] = validate(val) except: value = dict(zip(possible_keys, repeat(validate(value)))) return value
[docs]def validate_dataarray(val): if not isinstance(val, xr.DataArray): raise ValueError("Require xarray.DataArray, not %r" % type(val)) return val
[docs]def validate_marker(val): """Does not really make a validation because markers can be quite of different types""" if val is None: return None else: return safe_list(val)
[docs]def validate_alpha(val): '''Validate an alpha value between 0 and 1''' val = validate_float(val) if val < 0 or val > 1: raise ValueError('Alpha values must lay between 0 and 1!') return val
[docs]def validate_iter(value): """Validate that the given value is an iterable""" try: iter(value) except TypeError: raise ValueError("%s is not an iterable!" % repr(value)) else: return value
[docs]def validate_cbarpos(value): """Validate a colorbar position Parameters ---------- value: bool or str A string can be a combination of 'sh|sv|fl|fr|ft|fb|b|r' Returns ------- list list of strings with possible colorbar positions Raises ------ ValueError""" patt = 'sh|sv|fl|fr|ft|fb|b|r' if value is True: value = {'b'} elif not value: value = set() elif isinstance(value, six.string_types): for s in re.finditer('[^%s]+' % patt, value): warn("Unknown colorbar position %s!" % s.group(), RuntimeWarning) value = set(re.findall(patt, value)) else: value = validate_stringset(value) for s in (s for s in value if not re.match(patt, s)): warn("Unknown colorbar position %s!" % s) value.remove(s) return value
[docs]def validate_cmap(val): """Validate a colormap Parameters ---------- val: str or :class:`mpl.colors.Colormap` Returns ------- str or :class:`mpl.colors.Colormap` Raises ------ ValueError""" from matplotlib.colors import Colormap try: return validate_str(val) except ValueError: if not isinstance(val, Colormap): raise ValueError( "Could not find a valid colormap!") return val
[docs]def validate_cmaps(cmaps): """Validate a dictionary of color lists Parameters ---------- cmaps: dict a mapping from a colormap name to a list of colors Raises ------ ValueError If one of the values in `cmaps` is not a color list Notes ----- For all items (listname, list) in `cmaps`, the reversed list is automatically inserted with the ``listname + '_r'`` key.""" cmaps = {validate_str(key): validate_colorlist(val) for key, val in cmaps} for key, val in six.iteritems(cmaps): cmaps.setdefault(key + '_r', val[::-1]) return cmaps
[docs]def validate_sym_lims(val): validator = try_and_error(validate_none, ValidateInStrings( 'sym_links', ['min', 'max'], True)) val = safe_list(val) if len(val) != 2: val = val + val if not len(val) == 2: raise ValueError("Need two values for the symmetric limits, not %i" % ( len(val))) return list(map(validator, val))
valid_legend_locs = [ "best", "upper right", "upper left", "lower left", "lower right", "right", "center left", "center right", "lower center", "upper center", "center" ] validate_legend_loc = ValidateInStrings("legend_loc", valid_legend_locs, True)
[docs]def validate_legend(value): if isinstance(value, dict): return value try: return {'loc': validate_int(value)} except (ValueError, TypeError) as e: pass try: return {'loc': validate_legend_loc(value)} except (ValueError, TypeError) as e: pass value = validate_bool(value) return {'loc': 'best' if value else False}
[docs]def validate_lineplot(value): """Validate the value for the LinePlotter.plot formatoption Parameters ---------- value: None, str or list with mixture of both The value to validate""" if value is None: return value elif isinstance(value, six.string_types): return six.text_type(value) else: value = list(value) for i, v in enumerate(value): if v is None: pass elif isinstance(v, six.string_types): value[i] = six.text_type(v) else: raise ValueError('Expected None or string, found %s' % (v, )) return value
validate_ticklabels = try_and_error(validate_none, validate_str, validate_stringlist) validate_extend = ValidateInStrings('extend', ['neither', 'both', 'min', 'max'])
[docs]class ValidateList(object): """Validate a list of the specified `dtype` """ def __init__(self, dtype=None, length=None, listtype=list): """ Parameters ---------- dtype: object A datatype (e.g. :class:`float`) that shall be used for the conversion length: int The expected length of the list listtype: type The type to use for creating the list. Should accept any iterable """ #: data type (e.g. :class:`float`) used for the conversion self.dtype = dtype self.length = length self.listtype = list def __call__(self, l): """Validate whether `l` is a list with contents of :attr:`dtype` Parameters ---------- l: list-like Returns ------- list list with values of dtype :attr:`dtype` Raises ------ ValueError""" try: if self.dtype is None: validated = self.listtype(l) else: try: len(self.dtype) except TypeError: validated = self.listtype(map(self.dtype, l)) else: validated = self.listtype() for val in l: valid = False for dtype in self.dtype: try: validated.append(dtype(val)) except (TypeError, ValueError): pass else: valid = True break if not valid: raise ValueError( f"{val} cannot be converted to any of the " f"given data types: {self.dtype}!") except TypeError: if self.dtype is None: raise ValueError("Could not convert to list!") else: raise ValueError( "Could not convert to list of type %s!" % str(self.dtype)) if self.length is not None and len(validated) != self.length: raise ValueError('List with length %i is required! Not %i!' % ( self.length, len(validated))) return validated
[docs]def validate_err_calc(val): """Validation function for the :attr:`psy_simple.plotter.FldmeanPlotter.err_calc` formatoption""" try: val = validate_float(val) except (ValueError, TypeError): pass else: if val <= 100 and val >= 0: return val raise ValueError("Percentiles for the error calculation must lie " "between 0 and 100, not %s" % val) try: val = ValidateList(float, 2)(val) except (ValueError, TypeError): pass else: if all((v <= 100 and v >= 0) for v in val): return val raise ValueError("Percentiles for the error calculation must lie " "between 0 and 100, not %s" % val) try: val = validate_str(val) except ValueError: pass else: if 'std' not in val: raise ValueError( 'A string for the error calculation must contain std!') return val
[docs]class DictValValidator(object): """A validation class for formatoptions that expect dictionaries as values """ def __init__(self, key, valid, validators, default, ignorecase=False): """ Parameters ---------- key: str The name of the formatoption (will be used for error handling) valid: list of str The valid keys for the dictionary validators: func The validation function for the values of the dictionary default: object The default value to use if a key from `valid` is given in the provided value ignorecase: bool Whether the case of the keys should be ignored """ self.key = key self.valid = valid self.key_validator = ValidateInStrings(key, valid, ignorecase) self.default = default self.validate = validators def __call__(self, value): if isinstance(value, dict) and value and all( isinstance(key, six.string_types) for key in value): failed_key = False for key, val in list(six.iteritems(value)): try: new_key = self.key_validator(key) except ValueError: failed_key = True break else: value[new_key] = self.validate(value.pop(key)) if failed_key: if self.default is None: value = self.validate(value) value = dict(zip(self.valid, repeat(value))) else: value = {self.default: self.validate(value)} elif self.default is None: value = self.validate(value) value = dict(zip(self.valid, repeat(value))) else: value = {self.default: self.validate(value)} return value
[docs]class TicksValidator(ValidateInStrings): def __call__(self, val): # validate the ticks # if None, int or tuple (defining min- and max-range), pass if val is None or isinstance(val, int) or ( isinstance(val, tuple) and len(val) <= 3): return val # strings must be in the given list elif isinstance(val, six.string_types): return list(TicksType(val)) elif isinstance(val, dict): return list(TicksType(**val)) elif len(val) and isinstance(val[0], six.string_types): return list(TicksType(*val)) # otherwise we assume an array else: return ValidateList()(val)
[docs]class BoundsValidator: def __init__(self, type, default='rounded', possible_instances=None): """ For parameter description see :class:`matplotlib.rcsetup.ValidateInStrings`. Other Parameters ---------------- inis: tuple Tuple of object types that may pass the check default: str The default string to use for an integer (Default: 'rounded')""" self.type = type self.possible_instances = possible_instances self.default = default
[docs] def instance_check(self, val): if self.possible_instances: return isinstance(val, self.possible_instances) return False
def __call__(self, val): if val is None or self.instance_check(val): return val elif isinstance(val, dict): return list(self.type(**val)) elif isinstance(val, int): return list(self.type(self.default, val)) elif isinstance(val, six.string_types): return list(self.type(val)) elif isinstance(val[0], six.string_types): return list(self.type(*val)) # otherwise we assume an array else: return ValidateList(float)(val)
[docs]class LineWidthValidator(ValidateInStrings): def __call__(self, val): if val is None: return val elif isinstance(val, six.string_types): return [ValidateInStrings.__call__(self, val), 1.0] elif np.asarray(val).ndim and isinstance(val[0], six.string_types): return [ValidateInStrings.__call__(self, val[0])] + list(val[1:]) # otherwise we assume an array else: return np.asarray(val, float)
[docs]def validate_plot(val): validator = ValidateInStrings( '2d plot', ['mesh', 'contourf', 'contour', 'poly'], True) val = validator(val) return val
# ----------------------------------------------------------------------------- # ------------------------------ rcParams ------------------------------------- # ----------------------------------------------------------------------------- #: the :class:`~psyplot.config.rcsetup.RcParams` for the psy-simple plugin rcParams = RcParams(defaultParams={ # ------------------------------------------------------------------------- # ----------------------- Registered plotters ----------------------------- # ------------------------------------------------------------------------- 'project.plotters': [ {'simple': { 'module': 'psy_simple.plotters', 'plotter_name': 'SimplePlotterBase', 'plot_func': False, 'summary': ('All plotters that are visualized by the psy-simple ' 'package')}, 'lineplot': { 'module': 'psy_simple.plotters', 'plotter_name': 'LinePlotter', 'prefer_list': True, 'default_slice': None, 'summary': 'Make a line plot of one-dimensional data'}, 'fldmean': { 'module': 'psy_simple.plotters', 'plotter_name': 'FldmeanPlotter', 'prefer_list': True, 'default_slice': None, 'summary': 'Calculate and plot the mean over x- and y-dimensions' }, 'density': { 'module': 'psy_simple.plotters', 'plotter_name': 'DensityPlotter', 'prefer_list': False, 'default_slice': None, 'summary': 'Make a density plot of point data'}, 'barplot': { 'module': 'psy_simple.plotters', 'plotter_name': 'BarPlotter', 'prefer_list': True, 'default_slice': None, 'summary': 'Make a bar plot of one-dimensional data'}, 'violinplot': { 'module': 'psy_simple.plotters', 'plotter_name': 'ViolinPlotter', 'prefer_list': True, 'default_slice': None, 'summary': 'Make a violin plot of your data'}, 'plot2d': { 'module': 'psy_simple.plotters', 'plotter_name': 'Simple2DPlotter', 'prefer_list': False, 'default_slice': 0, 'default_dims': {'x': slice(None), 'y': slice(None)}, 'summary': 'Make a simple plot of a 2D scalar field'}, 'vector': { 'module': 'psy_simple.plotters', 'plotter_name': 'SimpleVectorPlotter', 'prefer_list': False, 'default_slice': 0, 'default_dims': {'x': slice(None), 'y': slice(None)}, 'summary': 'Make a simple plot of a 2D vector field', 'example_call': "filename, name=[['u_var', 'v_var']], ..."}, 'combined': { 'module': 'psy_simple.plotters', 'plotter_name': 'CombinedSimplePlotter', 'prefer_list': True, 'default_slice': 0, 'default_dims': {'x': slice(None), 'y': slice(None)}, 'summary': ('Plot a 2D scalar field with an overlying vector ' 'field'), 'example_call': ( "filename, name=[['my_variable', ['u_var', 'v_var']]], ...")}, }, validate_dict], # ------------------------------------------------------------------------- # --------------------- Default formatoptions ----------------------------- # ------------------------------------------------------------------------- 'plotter.baseplotter.tight': [False, validate_bool, 'fmt key for tight layout of the plots'], 'plotter.simple.grid': [ False, try_and_error(validate_bool_maybe_none, validate_color), 'fmt key to visualize the grid on simple plots (i.e. without ' 'projection)'], # labels 'plotter.baseplotter.title': [ '', six.text_type, 'fmt key to control the title of the axes'], 'plotter.baseplotter.figtitle': [ '', six.text_type, 'fmt key to control the title of the axes'], 'plotter.baseplotter.text': [ [], validate_text, 'fmt key to show text anywhere on the plot'], 'plotter.simple.ylabel': [ '', six.text_type, 'fmt key to modify the y-axis label for simple' 'plot (i.e. plots withouth projection)'], 'plotter.simple.xlabel': [ '', six.text_type, 'fmt key to modify the y-axis label for simple' 'plot (i.e. plots withouth projection)'], 'plotter.plot2d.clabel': [ '', six.text_type, 'fmt key to modify the colorbar label for 2D' 'plots'], # text sizes 'plotter.baseplotter.titlesize': [ 'large', validate_fontsize, 'fmt key for the fontsize of the axes title'], 'plotter.baseplotter.figtitlesize': [ 12, validate_fontsize, 'fmt key for the fontsize of the figure title'], 'plotter.simple.labelsize': [ 'medium', DictValValidator( 'labelsize', ['x', 'y'], validate_fontsize, None, True), 'fmt key for the fontsize of the x- and y-l abel of simple plots ' '(i.e. without projection)'], 'plotter.simple.ticksize': [ 'medium', DictValValidator( 'ticksize', ['major', 'minor'], validate_fontsize, 'major', True), 'fmt key for the fontsize of the ticklabels of x- and y-axis of ' 'simple plots (i.e. without projection)'], 'plotter.plot2d.cticksize': [ 'medium', validate_fontsize, 'fmt key for the fontsize of the ticklabels of the colorbar of 2D ' 'plots'], 'plotter.plot2d.clabelsize': [ 'medium', validate_fontsize, 'fmt key for the fontsize of the colorbar label'], # text weights 'plotter.baseplotter.titleweight': [ None, validate_fontweight, 'fmt key for the fontweight of the axes title'], 'plotter.baseplotter.figtitleweight': [ None, validate_fontweight, 'fmt key for the fontweight of the figure title'], 'plotter.simple.labelweight': [ None, DictValValidator( 'labelweight', ['x', 'y'], validate_fontweight, None, True), 'fmt key for the fontweight of the x- and y-l abel of simple plots ' '(i.e. without projection)'], 'plotter.simple.tickweight': [None, DictValValidator( 'tickweight', ['major', 'minor'], validate_fontweight, 'major', True), 'fmt key for the fontweight of the ticklabels of x- and y-axis of ' 'simple plots (i.e. without projection)'], 'plotter.plot2d.ctickweight': [ None, validate_fontweight, 'fmt key for the fontweight of the ticklabels of the colorbar of 2D ' 'plots'], 'plotter.plot2d.clabelweight': [ None, validate_fontweight, 'fmt key for the fontweight of the colorbar label'], # text properties 'plotter.baseplotter.titleprops': [ {}, validate_dict, 'fmt key for the additional properties of the title' ], 'plotter.baseplotter.figtitleprops': [ {}, validate_dict, 'fmt key for the additional properties of the figure title'], 'plotter.simple.labelprops': [{}, DictValValidator( 'labelprops', ['x', 'y'], validate_dict, None, True), 'fmt key for the additional properties of the x- and y-label'], 'plotter.simple.xtickprops': [ {'major': {}, 'minor': {}}, DictValValidator( 'xtickprops', ['major', 'minor'], validate_dict, 'major', True), 'fmt key for the additional properties of the ticklabels of x-axis'], 'plotter.simple.ytickprops': [ {'major': {}, 'minor': {}}, DictValValidator( 'ytickprops', ['major', 'minor'], validate_dict, 'major', True), 'fmt key for the additional properties of the ticklabels of y-axis'], 'plotter.plot2d.clabelprops': [ {}, validate_dict, 'fmt key for the additional properties of the colorbar label'], 'plotter.plot2d.ctickprops': [ {}, validate_dict, 'fmt key for the additional properties of the colorbar ticklabels'], # mask formatoptions 'plotter.baseplotter.background': [ 'rc', try_and_error(ValidateInStrings('background', ['rc']), validate_none, validate_color), "The background color for the plot"], 'plotter.baseplotter.mask': [ None, try_and_error(validate_none, validate_str, validate_dataarray)], 'plotter.baseplotter.maskleq': [ None, try_and_error(validate_none, validate_float), 'fmt key to mask values less or equal than a certain threshold'], 'plotter.baseplotter.maskless': [ None, try_and_error(validate_none, validate_float), 'fmt key to mask values less than a certain threshold'], 'plotter.baseplotter.maskgreater': [ None, try_and_error(validate_none, validate_float), 'fmt key to mask values greater than a certain threshold'], 'plotter.baseplotter.maskgeq': [ None, try_and_error(validate_none, validate_float), 'fmt key to mask values greater than or equal to a certain threshold'], 'plotter.baseplotter.maskbetween': [ None, try_and_error(validate_none, ValidateList(float, 2)), 'fmt key to mask values between a certain range'], # density plotter 'plotter.density.coord': [ None, try_and_error(validate_none, validate_dataarray, validate_str, validate_stringlist), 'Alternative x-coordinate to use for DensityPlotter'], 'plotter.density.xrange': [ 'minmax', validate_limits, 'The histogram limits of the density plot'], 'plotter.density.yrange': [ 'minmax', validate_limits, 'The histogram limits of the density plot'], 'plotter.density.precision': [ 0, try_and_error(validate_float, ValidateList((float, str), 2), validate_str), 'The precision of the data to make sure that the bin width is not ' 'below this value'], 'plotter.density.bins': [ 10, try_and_error(validate_int, ValidateList(int, 2)), 'The bins in x- and y-direction of the density plot'], 'plotter.density.normed': [ None, try_and_error(validate_none, ValidateInStrings( 'normed', ['area', 'counts', 'x', 'y', 'col', 'column', 'columns', 'row', 'rows'], True)), 'The normalization of the density histogram'], 'plotter.density.density': [ 'hist', ValidateInStrings('density', ['hist', 'kde'], True)], # axis color 'plotter.simple.axiscolor': [ None, validate_axiscolor, 'fmt key to modify the color of the spines'], # SimplePlot 'plotter.line.coord': [ None, try_and_error(validate_none, validate_dataarray, validate_str, validate_stringlist), 'Alternative x-coordinate to use for LinePlotter'], 'plotter.line.plot': [ '-', validate_lineplot, 'fmt key to modify the line style'], 'plotter.line.error': [ 'fill', try_and_error(ValidateInStrings('error', ['fill'], True), validate_none), 'The visualization type of the errors for line plots'], 'plotter.line.marker': [ None, validate_marker, 'The symbol of the marker'], 'plotter.line.markersize': [ None, try_and_error(validate_none, validate_float), 'The size of the marker'], 'plotter.line.linewidth': [ None, try_and_error(validate_none, validate_float), 'The widths of the lines'], 'plotter.line.erroralpha': [ 0.15, validate_alpha, 'The alpha value of the error range'], 'plotter.bar.coord': [ None, try_and_error(validate_none, validate_dataarray, validate_str, validate_stringlist), 'Alternative x-coordinate to use for BarPlotter'], 'plotter.bar.widths': [ 'equal', try_and_error( validate_float, ValidateInStrings( 'widths', ['equal', 'data'], True)), 'fmt key to change between equal and data given width of the bars'], 'plotter.bar.categorical': [ None, validate_bool_maybe_none, 'fmt key to change between categorical and non-categorical plotting'], 'plotter.bar.alpha': [ 1.0, validate_float, 'fmt key to control the transparency for the bar plots'], 'plotter.bar.plot': [ 'bar', validate_lineplot, 'fmt key to modify whether bar plots shall be stacked or not'], 'plotter.violin.plot': [ True, validate_bool_maybe_none, 'fmt key to modify whether violin plots shall be drawn'], 'plotter.simple.transpose': [ False, validate_bool, 'fmt key to switch x- and y-axis'], 'plotter.simple.color': [ None, try_and_error(validate_none, validate_cmap, validate_iter), 'fmt key to modify the color cycle simple plots'], 'plotter.simple.ylim': [ 'rounded', validate_limits, 'fmt key to specify the y-axis limits'], 'plotter.simple.xlim': [ 'rounded', validate_limits, 'fmt key to specify the x-axis limits'], 'plotter.simple.sym_lims': [ None, validate_sym_lims, 'fmt key to make symmetric x- and y-axis limits'], 'plotter.simple.xticks': [ {'major': None, 'minor': None}, DictValValidator( 'xticks', ['major', 'minor'], TicksValidator( 'xticks', tick_strings, True), 'major', True), 'fmt key to modify the x-axis ticks'], 'plotter.simple.yticks': [ {'major': None, 'minor': None}, DictValValidator( 'yticks', ['major', 'minor'], TicksValidator( 'yticks', tick_strings, True), 'major', True), 'fmt key to modify the y-axis ticks'], 'plotter.simple.xticklabels': [ None, DictValValidator('xticklabels', ['major', 'minor'], validate_ticklabels, 'major', True), 'fmt key to modify the x-axis ticklabels'], 'plotter.simple.yticklabels': [ None, DictValValidator('yticklabels', ['major', 'minor'], validate_ticklabels, 'major', True), 'fmt key to modify the y-axis ticklabels'], 'plotter.simple.xrotation': [ 0, validate_float, 'fmt key to modify the rotation of the x-axis ticklabels'], 'plotter.simple.yrotation': [ 0, validate_float, 'fmt key to modify the rotation of the x-axis ticklabels'], 'plotter.simple.legendlabels': [ '%(arr_name)s', try_and_error( validate_str, ValidateList(six.text_type)), 'fmt key to modify the legend labels'], 'plotter.simple.legend': [ True, validate_legend, 'fmt key to draw a legend'], # FldmeanPlotter 'plotter.fldmean.mean': [ 'mean', try_and_error( ValidateInStrings('mean', ['mean', 'median'], True), validate_float), "The calculation result, either the 'mean', 'median' or a percentile"], 'plotter.fldmean.err_calc': [ 'std', validate_err_calc, "The error calculation method, either the 'std' or a minimum " "and maximum percentile"], # Plot2D 'plotter.plot2d.interp_bounds': [ None, validate_bool_maybe_none, 'Switch to interpolate the bounds for 2D plots'], 'plotter.plot2d.plot': [ 'mesh', try_and_error(validate_none, validate_plot), 'fmt key to specify the plot type of 2D scalar plots'], 'plotter.plot2d.plot.min_circle_ratio': [ 0.05, validate_float, 'fmt key to specify the min_circle_ratio that is used to mask very ' ' flat triangles in a triangular plot'], 'plotter.plot2d.cbar': [ ['b'], validate_cbarpos, 'fmt key to specify the position of the colorbar'], 'plotter.plot2d.cbarspacing': [ 'uniform', validate_str, 'fmt key to specify the spacing of the colorbar'], 'plotter.plot2d.miss_color': [ None, try_and_error(validate_none, validate_color), 'fmt key to specify the color of missing values'], 'plotter.plot2d.cmap': [ 'white_blue_red', validate_cmap, 'fmt key to specify the colormap'], 'plotter.plot2d.cticks': [ None, try_and_error(validate_none, BoundsValidator( CTicksType, default='bounds')), 'fmt key to specify the ticks of the colorbar'], 'plotter.plot2d.cticklabels': [ None, validate_ticklabels, 'fmt key to specify the ticklabels of the colorbar'], 'plotter.plot2d.extend': [ 'neither', validate_extend, 'fmt key to specify the style of the colorbar on minimum and maximum'], 'plotter.plot2d.bounds': [ 'rounded', BoundsValidator(BoundsType, 'bounds', mpl.colors.Normalize), 'fmt key to specify bounds and norm of the colorbar'], 'plotter.plot2d.levels': [ None, BoundsValidator(BoundsType), 'fmt key to specify the levels for a contour plot'], # TODO: Implement opacity # 'plotter.plot2d.opacity': [None, try_and_error(validate_none, # validate_opacity)], 'plotter.plot2d.datagrid': [ None, try_and_error(validate_none, validate_dict, validate_str), 'fmt key to plot the lines of the data grid'], 'plotter.plot2d.mask_datagrid': [ True, validate_bool, 'fmt key to mask cells with NaN when plotting the data grid'], # VectorPlot 'plotter.vector.plot': [ 'quiver', try_and_error(validate_none, ValidateInStrings( '2d plot', ['quiver', 'stream'], True)), 'fmt key for the plot type of vector plots'], 'plotter.vector.arrowsize': [ None, try_and_error(validate_none, validate_float), 'fmt key for the size of the arrows on vector plots'], 'plotter.vector.arrowstyle': [ '-|>', ValidateInStrings('arrowstyle', ArrowStyle._style_list), 'fmt key for the style of the arrows on stream plots'], 'plotter.vector.density': [ 1.0, try_and_error(validate_float, ValidateList(float, 2)), 'fmt key for the density of arrows on a vector plot'], 'plotter.vector.linewidth': [ None, LineWidthValidator('linewidth', ['absolute', 'u', 'v'], True), 'fmt key for the linewidths of the arrows'], 'plotter.vector.color': [ 'k', try_and_error(validate_float, validate_color, ValidateInStrings( 'color', ['absolute', 'u', 'v'], True)), 'fmt key for the colors of the arrows'], # default texts 'texts.labels': [{'tinfo': '%H:%M', 'dtinfo': '%B %d, %Y. %H:%M', 'dinfo': '%B %d, %Y', 'desc': '%(long_name)s [%(units)s]', 'sdesc': '%(name)s [%(units)s]'}, validate_dict, 'labels that shall be replaced in TextBase formatoptions', ' (e.g. the title formatoption) when inserted within ' 'curly braces ({}))'], 'texts.default_position': [(1., 1.), ValidateList(float, 2), 'default position for the text fmt key'], 'texts.delimiter': [', ', validate_str, 'default delimiter to separate netCDF meta attributes ' 'when displayed on the plot'], # ------------------------------------------------------------------------- # ---------------------------- Miscallaneous ------------------------------ # ------------------------------------------------------------------------- # color lists for user-defined colormaps (see for example # psy_simple.colors._cmapnames) 'colors.cmaps': [ {}, validate_cmaps, 'User defined color lists that shall be accessible through the ' ':meth:`psyplot.plotter.colors.get_cmap` function'], 'widgets.colors.cmaps': [ ["viridis", "Reds", "Blues", "Greens", "binary", "RdBu", "coolwarm", "red_white_blue", "winter", "jet", "white_blue_red", "gist_ncar", "gist_earth", "Paired", "gnuplot", "gnuplot2"], validate_stringlist, 'Colormaps that should be listed in the context menu of the cmap ' 'button'], 'ticks.which': ['major', ValidateInStrings( 'ticks.which', ['major', 'minor'], True), 'default tick that is used when using a x- or y-tick formatoption'], }) # add combinedplotter strings for vectorplot _subd = SubDict(rcParams.defaultParams, ['plotter.vector.', 'plotter.plot2d.']) for _key in ['plot', 'cbar', 'cmap', 'bounds', 'cticksize', 'cbarspacing', 'ctickweight', 'ctickprops', 'clabel', 'cticks', 'cticklabels', 'clabelsize', 'clabelprops', 'clabelweight']: rcParams.defaultParams['plotter.combinedsimple.v%s' % _key] = _subd[_key] rcParams.defaultParams['plotter.combinedsimple.plot'] = rcParams.defaultParams[ 'plotter.plot2d.plot'] del _key, _subd rcParams.update_from_defaultParams()