import numpy as np
from contextlib import contextmanager
import importlib
from ..utilities import deprecated
def _importorskip(modname):
try:
importlib.import_module(modname) # noqa: E402
has = True
except ImportError:
has = False
return has
has_mpl = _importorskip("matplotlib")
has_cartopy = _importorskip("cartopy")
has_seaborn = _importorskip("seaborn")
has_ipython = _importorskip("IPython")
has_ipywidgets = _importorskip("ipywidgets")
STYLE = {"axes": "whitegrid", "palette": "Set1"} # Default styles
if has_mpl:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.cm as cm
import matplotlib.colors as mcolors
if has_cartopy:
import cartopy
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
land_feature = cfeature.NaturalEarthFeature(
category="physical", name="land", scale="50m", facecolor=[0.4, 0.6, 0.7]
)
else:
land_feature = ()
if has_seaborn:
STYLE["axes"] = "dark"
import seaborn as sns
@contextmanager
def axes_style(style: str = STYLE["axes"]):
""" Provide a context for plots
The point is to handle the availability of :mod:`seaborn` or not and to be able to use::
with axes_style(style):
fig, ax = plt.subplots()
in all situations.
"""
if has_seaborn: # Execute within a seaborn context:
with sns.axes_style(style):
yield
else: # Otherwise do nothing
yield
[docs]@deprecated("The 'discrete_coloring' plotting utility is deprecated since 0.1.13. It's been replaced by 'ArgoColors'. Calling it will raise an error after argopy 0.1.14")
class discrete_coloring:
""" Handy class to manage discrete coloring and the associated colorbar
Warnings
--------
This plotting utility is deprecated since 0.1.13. It's been replaced by :class:`argopy.plot.ArgoColors`. Calling
it will raise an error after argopy 0.1.14.
Examples
--------
This class can be used like this:
::
year_range = np.arange(2002,2010)
dc = discrete_coloring(name='Spectral', N=len(year_range) )
plt.scatter(this['LONGITUDE'], this['LATITUDE'], c=this['TIME.year'],
cmap=dc.cmap, vmin=year_range[0], vmax=year_range[-1])
dc.cbar(ticklabels=yr_range, fraction=0.03, label='Years')
"""
def __init__(self, name="Set1", N=12):
"""
Parameters
----------
name: str
Name if the colormap to use. Default: 'Set1'
N: int
Number of colors to reduce the colormap to. Default: 12
"""
self.name = name
self.Ncolors = N
@property
def cmap(self):
"""Return a discrete colormap from a quantitative or continuous colormap name
Returns
-------
:class:`matplotlib.colors.LinearSegmentedColormap`
"""
name = self.name
K = self.Ncolors
if name in [
"Set1",
"Set2",
"Set3",
"Pastel1",
"Pastel2",
"Paired",
"Dark2",
"Accent",
]:
# Segmented (or quantitative) colormap:
N_ref = {
"Set1": 9,
"Set2": 8,
"Set3": 12,
"Pastel1": 9,
"Pastel2": 8,
"Paired": 12,
"Dark2": 8,
"Accent": 8,
}
N = N_ref[name]
cmap = plt.get_cmap(name=name)
colors_i = np.concatenate(
(np.linspace(0, 1.0, N), (0.0, 0.0, 0.0, 0.0)), axis=0
)
cmap = cmap(colors_i) # N x 4
n = np.arange(0, N)
new_n = n.copy()
if K > N:
for k in range(N, K):
r = np.roll(n, -k)[0][np.newaxis]
new_n = np.concatenate((new_n, r), axis=0)
new_cmap = cmap.copy()
new_cmap = cmap[new_n, :]
new_cmap = mcolors.LinearSegmentedColormap.from_list(
name + "_%d" % K, colors=new_cmap, N=K
)
elif name == "Month":
clist = [
"darkslateblue",
"skyblue",
"powderblue",
"honeydew",
"lemonchiffon",
"pink",
"salmon",
"deeppink",
"gold",
"chocolate",
"darkolivegreen",
"cadetblue",
]
cmap = mcolors.LinearSegmentedColormap.from_list("my_colormap", clist)
N = 12
colors_i = np.concatenate((np.linspace(0, 1.0, N), (0.0, 0.0, 0.0, 0.0)))
colors_rgba = cmap(colors_i)
indices = np.linspace(0, 1.0, N + 1)
cdict = {}
for ki, key in enumerate(("red", "green", "blue")):
cdict[key] = [
(indices[i], colors_rgba[i - 1, ki], colors_rgba[i, ki])
for i in np.arange(N + 1)
]
new_cmap = mcolors.LinearSegmentedColormap("month_%d" % N, cdict, N)
else:
# Continuous colormap:
N = K
cmap = plt.get_cmap(name=name)
colors_i = np.concatenate((np.linspace(0, 1.0, N), (0.0, 0.0, 0.0, 0.0)))
colors_rgba = cmap(colors_i) # N x 4
indices = np.linspace(0, 1.0, N + 1)
cdict = {}
for ki, key in enumerate(("red", "green", "blue")):
cdict[key] = [
(indices[i], colors_rgba[i - 1, ki], colors_rgba[i, ki])
for i in np.arange(N + 1)
]
# Return colormap object.
new_cmap = mcolors.LinearSegmentedColormap(cmap.name + "_%d" % N, cdict, N)
self._colormap = new_cmap
return new_cmap
def cbar(self, ticklabels=None, **kwargs):
"""Return a colorbar with adjusted tick labels
Returns
-------
:class:`matplotlib.pyplot.colorbar`
"""
cmap = self.cmap
ncolors = self.Ncolors
mappable = cm.ScalarMappable(cmap=cmap)
mappable.set_array([])
mappable.set_clim(-0.5, ncolors + 0.5)
colorbar = plt.colorbar(mappable, **kwargs)
colorbar.set_ticks(np.linspace(0, ncolors, ncolors))
colorbar.set_ticklabels(ticklabels)
self._colorbar = colorbar
return colorbar
def to_rgba(self, range, value):
""" Return the RGBA color for a given value of the colormap and a range """
norm = mpl.colors.Normalize(vmin=range[0], vmax=range[-1])
scalarMap = cm.ScalarMappable(norm=norm, cmap=self.cmap)
return scalarMap.to_rgba(value)
[docs]def latlongrid(ax, dx="auto", dy="auto", fontsize="auto", label_style_arg={}, **kwargs):
""" Add latitude/longitude grid line and labels to a cartopy geoaxes
Parameters
----------
ax: cartopy.mpl.geoaxes.GeoAxesSubplot
Cartopy axes to add the lat/lon grid to
dx: 'auto' or float
Grid spacing along longitude
dy: 'auto' or float
Grid spacing along latitude
fontsize: 'auto' or int
Grid label font size
Returns
-------
class:`cartopy.mpl.geoaxes.GeoAxesSubplot.gridlines`
"""
if not isinstance(ax, cartopy.mpl.geoaxes.GeoAxesSubplot):
raise ValueError("Please provide a cartopy.mpl.geoaxes.GeoAxesSubplot instance")
defaults = {"linewidth": 0.5, "color": "gray", "alpha": 0.5, "linestyle": ":"}
gl = ax.gridlines(crs=ax.projection, draw_labels=True, **{**defaults, **kwargs})
if dx != "auto":
gl.xlocator = mticker.FixedLocator(np.arange(-180, 180 + 1, dx))
if dy != "auto":
gl.ylocator = mticker.FixedLocator(np.arange(-90, 90 + 1, dy))
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
# Cartopy <= 0.18:
# gl.xlabels_top = False
# gl.ylabels_right = False
# Cartopy >= 0.18:
gl.top_labels = False
gl.right_labels = False
label_style_arg_defaults = {"fontsize": None}
if fontsize != "auto":
label_style_arg_defaults = {"fontsize": fontsize}
gl.xlabel_style = {**label_style_arg_defaults, **label_style_arg}
gl.ylabel_style = {**label_style_arg_defaults, **label_style_arg}
return gl