Skip to content

Commit ad00933

Browse files
committed
Saturate image when robust=True
Carefully, so that saturation is always consistent across color channels and facets of big plots.
1 parent 0e9c3f6 commit ad00933

File tree

2 files changed

+28
-4
lines changed

2 files changed

+28
-4
lines changed

xarray/plot/plot.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from datetime import datetime
1717

1818
from .utils import (_determine_cmap_params, _infer_xy_labels, get_axis,
19-
import_matplotlib_pyplot)
19+
import_matplotlib_pyplot, ROBUST_PERCENTILE)
2020
from .facetgrid import FacetGrid
2121
from xarray.core.pycompat import basestring
2222

@@ -419,14 +419,32 @@ def newplotfunc(darray, x=None, y=None, figsize=None, size=None,
419419
# Decide on a default for the colorbar before facetgrids
420420
if add_colorbar is None:
421421
add_colorbar = plotfunc.__name__ != 'contour'
422-
if plotfunc.__name__ == 'imshow' and \
423-
darray.ndim == (3 + (row is not None) + (col is not None)):
422+
imshow_rgb = plotfunc.__name__ == 'imshow' and \
423+
darray.ndim == (3 + (row is not None) + (col is not None))
424+
if imshow_rgb:
424425
# Don't add a colorbar when showing an image with explicit colors
425426
add_colorbar = False
427+
# Manually stretch colors for robust cmap
428+
if robust:
429+
flat = darray.values.ravel(order='K')
430+
flat = flat[~np.isnan(flat)]
431+
if flat.size == 0:
432+
# All data will be masked, so skip percentile calculation
433+
vmin, vmax = 0, 1
434+
if vmin is None:
435+
vmin = np.percentile(flat, ROBUST_PERCENTILE)
436+
if vmax is None:
437+
vmax = np.percentile(flat, 100 - ROBUST_PERCENTILE)
438+
darray = (darray - vmin) / (vmax - vmin)
439+
robust = False
440+
del flat
441+
# Clip range to [0, 1] to avoid visual artefacts
442+
darray.values[:] = np.clip(darray.values, 0, 1)
426443

427444
# Handle facetgrids first
428445
if row or col:
429446
allargs = locals().copy()
447+
allargs.pop('imshow_rgb')
430448
allargs.update(allargs.pop('kwargs'))
431449

432450
# Need the decorated plotting function
@@ -575,6 +593,11 @@ def imshow(x, y, z, ax, **kwargs):
575593
While other plot methods require the DataArray to be strictly
576594
two-dimensional, ``imshow`` also accepts a 3D array where the third
577595
dimension can be interpreted as RGB or RGBA color channels.
596+
In this case, ``robust=True`` will saturate the image in the
597+
usual way, consistenly between all bands and facets.
598+
599+
This method will clip oversaturated pixels to the valid range,
600+
instead of wrapping around to new colors like matplotlib.
578601
579602
.. note::
580603
This function needs uniformly spaced coordinates to

xarray/plot/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from ..core.pycompat import basestring
1111
from ..core.utils import is_scalar
1212

13+
ROBUST_PERCENTILE = 2.0
14+
1315

1416
def _load_default_cmap(fname='default_colormap.csv'):
1517
"""
@@ -160,7 +162,6 @@ def _determine_cmap_params(plot_data, vmin=None, vmax=None, cmap=None,
160162
cmap_params : dict
161163
Use depends on the type of the plotting function
162164
"""
163-
ROBUST_PERCENTILE = 2.0
164165
import matplotlib as mpl
165166

166167
calc_data = np.ravel(plot_data[~pd.isnull(plot_data)])

0 commit comments

Comments
 (0)