Source code for argopy.plotters

#!/bin/env python
# -*coding: UTF-8 -*-
#
# We try to import depedencies and catch missing module errors in order to avoid to load argopy just because
# Matplotlib is not installed.
#
# Decorator warnUnless is mandatory
#

import numpy as np
import warnings
from argopy.errors import InvalidDashboard


try:
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import matplotlib.ticker as mticker
    import matplotlib.cm as cm
    import matplotlib.colors as mcolors
    with_matplotlib = True
except ModuleNotFoundError:
    warnings.warn("argopy requires matplotlib installed for any plotting functionality")
    with_matplotlib = False

try:
    import cartopy
    import cartopy.crs as ccrs
    import cartopy.feature as cfeature
    from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
    with_cartopy = True
except ModuleNotFoundError:
    warnings.warn("argopy requires cartopy installed for full map plotting functionality")
    with_cartopy = False

try:
    import seaborn as sns
    sns.set_style("dark")
    with_seaborn = True
except ModuleNotFoundError:
    warnings.warn("argopy requires seaborn installed for full plotting functionality")
    with_seaborn = False

if with_cartopy:
    land_feature = cfeature.NaturalEarthFeature(category='physical', name='land',
                                                scale='50m', facecolor=[0.4, 0.6, 0.7])


[docs]def open_dashboard(wmo=None, cyc=None, width="100%", height=1000, url=None, type='ea'): """ Insert in a notebook the Euro-Argo dashboard page Parameters ---------- wmo: int The float WMO to display. By default, this is set to None and will insert the general dashboard. Returns ------- IFrame: IPython.lib.display.IFrame """ if type not in ['ea']: raise InvalidDashboard("Invalid dashboard type") from IPython.display import IFrame if url is None: if type == 'ea': # Open Euro-Argo dashboard if wmo is None: url = "https://fleetmonitoring.euro-argo.eu" else: url = "https://fleetmonitoring.euro-argo.eu/float/{}".format(str(wmo)) # # Note that argovis doesn't allow X-Frame insertion ! # elif type == 'argovis': # if cyc is None: # url = "https://argovis.colorado.edu/catalog/platforms/{}/page".format(str(wmo)) # else: # url = "https://argovis.colorado.edu/catalog/profiles/{}_{}/page".format(str(wmo),str(cyc)) return IFrame(url, width=width, height=height)
class discrete_coloring(): """ Handy class to manage discrete coloring and the associated colorbar Example: 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): self.name = name self.Ncolors = N @property def cmap(self): """Return a discrete colormap from a quantitative or continuous colormap name name: name of the colormap, eg 'Paired' or 'jet' K: number of colors in the final discrete colormap """ 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., N), (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., N), (0., 0., 0., 0.))) colors_rgba = cmap(colors_i) indices = np.linspace(0, 1., 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., N), (0., 0., 0., 0.))) colors_rgba = cmap(colors_i) # N x 4 indices = np.linspace(0, 1., 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""" 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) def latlongrid(ax, dx=5., dy=5., fontsize=6, **kwargs): """ Add latitude/longitude grid line and labels to a cartopy geoaxes """ if not isinstance(ax, cartopy.mpl.geoaxes.GeoAxesSubplot): raise ValueError("Please provide a cartopy.mpl.geoaxes.GeoAxesSubplot instance") defaults = {'linewidth': .5, 'color': 'gray', 'alpha': 0.5, 'linestyle': '--'} gl = ax.gridlines(crs=ax.projection, draw_labels=True, **{**defaults, **kwargs}) gl.xlocator = mticker.FixedLocator(np.arange(-180, 180+1, dx)) gl.ylocator = mticker.FixedLocator(np.arange(-90, 90+1, dy)) gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER gl.xlabels_top = False gl.xlabel_style = {'fontsize': fontsize} gl.ylabels_right = False gl.ylabel_style = {'fontsize': fontsize} return gl def warnUnless(ok, txt): def inner(fct): def wrapper(*args, **kwargs): warnings.warn("%s %s" % (fct.__name__, txt)) return fct(*args, **kwargs) return wrapper if not ok: return inner else: return lambda f: f @warnUnless(with_matplotlib and with_cartopy and with_seaborn, "requires matplotlib, cartopy and seaborn installed") def plot_trajectory(idx): """ Plot trajectories for an index dataframe """ if not with_seaborn: raise BaseException("This function requires seaborn") fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree()) ax.add_feature(land_feature, edgecolor='black') nfloat = len(idx.groupby('wmo').first()) mypal = sns.color_palette("bright", nfloat) sns.lineplot(x="longitude", y="latitude", hue="wmo", data=idx, sort=False, palette=mypal, legend=False) sns.scatterplot(x="longitude", y="latitude", hue='wmo', data=idx, palette=mypal) # width = np.abs(idx['longitude'].max()-idx['longitude'].min()) # height = np.abs(idx['latitude'].max()-idx['latitude'].min()) # extent = (idx['longitude'].min()-width/4, # idx['longitude'].max()+width/4, # idx['latitude'].min()-height/4, # idx['latitude'].max()+height/4) gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.7, linestyle=':') gl.xlabels_top = False gl.ylabels_left = False gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER # ax.set_extent(extent) plt.legend(loc='upper right', bbox_to_anchor=(1.25, 1)) if (nfloat > 15): ax.get_legend().remove() return fig, ax @warnUnless(with_matplotlib and with_cartopy and with_seaborn, "requires matplotlib, cartopy and seaborn installed") def plot_dac(idx): """ Histogram of DAC for an index dataframe """ if not with_seaborn: raise BaseException("This function requires seaborn") fig = plt.figure(figsize=(10, 5)) mind = idx.groupby('institution').size().sort_values(ascending=False).index sns.countplot(y='institution', data=idx, order=mind) plt.ylabel('number of profiles') return fig @warnUnless(with_matplotlib and with_cartopy and with_seaborn, "requires matplotlib, cartopy and seaborn installed") def plot_profilerType(idx): """ Histogram of profile types for an index dataframe """ if not with_seaborn: raise BaseException("This function requires seaborn") fig = plt.figure(figsize=(10, 5)) mind = idx.groupby('profiler_type').size().sort_values(ascending=False).index sns.countplot(y='profiler_type', data=idx, order=mind) plt.xlabel('number of profiles') plt.ylabel('') return fig