import cartopy.crs as ccrs
import numpy as np
from pyaerocom.mathutils import exponent
[docs]
def projection_from_str(projection_str="PlateCarree"):
"""Return instance of cartopy projection class based on string ID"""
try:
return ccrs.__dict__[projection_str]()
except KeyError:
raise ValueError(f"no such projection {projection_str}")
[docs]
def custom_mpl(mpl_rcparams=None, default_large=True, **kwargs):
"""Custom matplotlib settings"""
if mpl_rcparams is None:
from matplotlib import rcParams as mpl_rcparams
small = 10
medium = 12
big = 14
huge = 18
if default_large:
default = {
"font.size": huge,
"axes.titlesize": huge,
"axes.labelsize": huge,
"xtick.labelsize": big,
"ytick.labelsize": big,
"legend.fontsize": big,
"figure.titlesize": huge,
}
else:
default = {
"font.size": small,
"axes.titlesize": big,
"axes.labelsize": medium,
"xtick.labelsize": medium,
"ytick.labelsize": medium,
"legend.fontsize": small,
"figure.titlesize": big,
}
for k, v in default.items():
try:
mpl_rcparams[k] = kwargs[k]
except Exception:
mpl_rcparams[k] = v
return mpl_rcparams
[docs]
def calc_figsize(lon_range, lat_range, figh=8):
"""Calculate figure size based on data
The required figure width is computed based on the input height and the
aspect ratio of the longitude and latitude arrays
Parameters
----------
lon_range : tuple
2-element tuple specifying longitude range (may also be list or array)
lat_range : tuple
2-element tuple specifying latitude range (may also be list or array)
figh : int
figure height in inches
add_cbar : bool
if True, the width is adapted accordingly
Returns
-------
tuple
2-element tuple containing figure width and height
"""
wfac = (lon_range[1] - lon_range[0]) / (lat_range[1] - lat_range[0])
figw = int(wfac * figh)
return (figw, figh)
[docs]
def calc_pseudolog_cmaplevels(vmin, vmax, add_zero=False):
"""Initiate pseudo-log discrete colormap levels
Parameters
----------
vmin : float
lower end of colormap (e.g. minimum value of data)
vmax : float
upper value of colormap (e.g. maximum value of data)
add_zero : bool
if True, the lower bound is set to 0 (irrelevant if vmin is 0).
Returns
-------
list
list containing boundary array for discrete colormap (e.g. using
BoundaryNorm)
Example
-------
>>> vmin, vmax = 0.02, 0.75
>>> vals = calc_pseudolog_cmaplevels(vmin, vmax, num_per_mag=10, add_zero=True)
>>> for val in vals: print("%.4f" %val)
0.0000
0.0100
0.0126
0.0158
0.0200
0.0251
0.0316
0.0398
0.0501
0.0631
0.0794
0.1000
"""
if vmin < 0:
vmin = 0
if vmin == 0:
vmin = 1 * 10.0 ** (exponent(vmax) - 2)
if not add_zero:
add_zero = True
elif vmax < vmin:
raise ValueError("Error: vmax must exceed vmin")
bounds = [0] if add_zero else []
low = float(exponent(vmin))
high = float(exponent(vmax))
bounds.extend(np.arange(np.floor(vmin * 10 ** (-low)), 10, 1) * 10.0 ** (low))
if low == high:
return bounds
for mag in range(int(low + 1), int(high)):
bounds.extend(np.linspace(1, 9, 9) * 10 ** (mag))
bounds.extend(np.arange(1, np.ceil(vmax * 10 ** (-high)), 1) * 10.0 ** (high))
bounds.append(vmax)
return bounds
[docs]
def get_cmap_levels_auto(vmin, vmax, num_per_mag=10):
"""Initiate pseudo-log discrete colormap levels
Note
----
This is a beta version and aims to
Parameters
----------
vmin : float
lower end of colormap (e.g. minimum value of data)
vmax : float
upper value of colormap (e.g. maximum value of data)
"""
high = float(exponent(vmax))
low = -3.0 if vmin == 0 else float(exponent(vmin))
lvls = []
if 1 % vmax * 10 ** (-high) == 0:
low += 1
high -= 1
for mag in range(int(low), int(high)):
lvls.extend(np.linspace(1, 10, num_per_mag - 1, endpoint=0) * 10 ** (mag))
lvls.extend(np.linspace(1, vmax * 10 ** (-high), num_per_mag, endpoint=1) * 10 ** (high))
return lvls
[docs]
def get_cmap_ticks_auto(lvls, num_per_mag=3):
"""Compute cmap ticks based on cmap levels
The cmap levels may be computed automatically using
:func:`get_cmap_levels_auto`.
Parameters
----------
lvls : list
list containing colormap levels
num_per_mag : int
desired number of ticks per magnitude
"""
low = exponent(lvls[1]) # second entry (first is 0)
vmax = lvls[-1]
high = exponent(vmax)
ticks = [0]
if 1 % vmax * 10 ** (-high) == 0:
for mag in range(low, high - 1):
ticks.extend(np.linspace(1, 10, num_per_mag, endpoint=0) * 10 ** (mag))
ticks.extend(np.linspace(1, 10, num_per_mag + 1, endpoint=1) * 10 ** (high - 1))
else:
for mag in range(low, high):
ticks.extend(np.linspace(1, 10, num_per_mag, endpoint=0) * 10 ** (mag))
ticks.extend(
np.linspace(1, vmax * 10 ** (-high), num_per_mag + 1, endpoint=1) * 10 ** (high)
)
return ticks