Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add getters and _repr_html_ for over/under/bad values of Colormap objects. #17900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/users/next_whats_new/colormap_get_under_over_bad.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Get under/over/bad colors of Colormap objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`matplotlib.colors.Colormap` now has methods
`~.colors.Colormap.get_under`, `~.colors.Colormap.get_over`,
`~.colors.Colormap.get_bad` for the colors used for out-of-range and masked
values.
69 changes: 55 additions & 14 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def __delitem__(self, key):
_colors_full_map.update(BASE_COLORS)
_colors_full_map = _ColorMapping(_colors_full_map)

_REPR_PNG_SIZE = (512, 64)


def get_named_colors_mapping():
"""Return the global mapping of names to named colors."""
Expand Down Expand Up @@ -614,26 +616,40 @@ def __copy__(self):
cmapobject._global = False
return cmapobject

def get_bad(self):
"""Get the color for masked values."""
if not self._isinit:
self._init()
return self._lut[self._i_bad]

def set_bad(self, color='k', alpha=None):
"""Set the color for masked values."""
_warn_if_global_cmap_modified(self)
self._rgba_bad = to_rgba(color, alpha)
if self._isinit:
self._set_extremes()

def get_under(self):
"""Get the color for low out-of-range values."""
if not self._isinit:
self._init()
return self._lut[self._i_under]

def set_under(self, color='k', alpha=None):
"""
Set the color for low out-of-range values when ``norm.clip = False``.
"""
"""Set the color for low out-of-range values."""
_warn_if_global_cmap_modified(self)
self._rgba_under = to_rgba(color, alpha)
if self._isinit:
self._set_extremes()

def get_over(self):
"""Get the color for high out-of-range values."""
if not self._isinit:
self._init()
return self._lut[self._i_over]

def set_over(self, color='k', alpha=None):
"""
Set the color for high out-of-range values when ``norm.clip = False``.
"""
"""Set the color for high out-of-range values."""
_warn_if_global_cmap_modified(self)
self._rgba_over = to_rgba(color, alpha)
if self._isinit:
Expand All @@ -655,6 +671,7 @@ def _init(self):
raise NotImplementedError("Abstract class only")

def is_gray(self):
"""Return whether the color map is grayscale."""
if not self._isinit:
self._init()
return (np.all(self._lut[:, 0] == self._lut[:, 1]) and
Expand Down Expand Up @@ -685,8 +702,8 @@ def reversed(self, name=None):

def _repr_png_(self):
"""Generate a PNG representation of the Colormap."""
IMAGE_SIZE = (400, 50)
X = np.tile(np.linspace(0, 1, IMAGE_SIZE[0]), (IMAGE_SIZE[1], 1))
X = np.tile(np.linspace(0, 1, _REPR_PNG_SIZE[0]),
(_REPR_PNG_SIZE[1], 1))
pixels = self(X, bytes=True)
png_bytes = io.BytesIO()
title = self.name + ' color map'
Expand All @@ -703,12 +720,36 @@ def _repr_html_(self):
"""Generate an HTML representation of the Colormap."""
png_bytes = self._repr_png_()
png_base64 = base64.b64encode(png_bytes).decode('ascii')
return ('<strong>' + self.name + '</strong>' +
'<img ' +
'alt="' + self.name + ' color map" ' +
'title="' + self.name + '"' +
'style="border: 1px solid #555;" ' +
'src="data:image/png;base64,' + png_base64 + '">')
def color_block(color):
hex_color = to_hex(color, keep_alpha=True)
return (f'<div title="{hex_color}" '
'style="display: inline-block; '
'width: 1em; height: 1em; '
'margin: 0; '
'vertical-align: middle; '
'border: 1px solid #555; '
f'background-color: {hex_color};"></div>')

return ('<div style="vertical-align: middle;">'
f'<strong>{self.name}</strong> '
'</div>'
'<div class="cmap"><img '
f'alt="{self.name} color map" '
f'title="{self.name}" '
'style="border: 1px solid #555;" '
f'src="data:image/png;base64,{png_base64}"></div>'
'<div style="vertical-align: middle; '
f'max-width: {_REPR_PNG_SIZE[0]+2}px; '
'display: flex; justify-content: space-between;">'
'<div style="float: left;">'
f'{color_block(self.get_under())} under'
'</div>'
'<div style="margin: 0 auto; display: inline-block;">'
f'bad {color_block(self.get_bad())}'
'</div>'
'<div style="float: right;">'
f'over {color_block(self.get_over())}'
'</div>')


class LinearSegmentedColormap(Colormap):
Expand Down
19 changes: 19 additions & 0 deletions lib/matplotlib/tests/test_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np
from PIL import Image
import pytest
import base64

from numpy.testing import assert_array_equal, assert_array_almost_equal

Expand Down Expand Up @@ -303,6 +304,9 @@ def test_BoundaryNorm():
cmref.set_under('white')
cmshould = mcolors.ListedColormap(['white', 'blue', 'red', 'black'])

assert mcolors.same_color(cmref.get_over(), 'black')
assert mcolors.same_color(cmref.get_under(), 'white')

refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
mynorm = mcolors.BoundaryNorm(bounds, cmshould.N, extend='both')
assert mynorm.vmin == refnorm.vmin
Expand All @@ -323,6 +327,8 @@ def test_BoundaryNorm():
cmref.set_under('white')
cmshould = mcolors.ListedColormap(['white', 'blue', 'red'])

assert mcolors.same_color(cmref.get_under(), 'white')

assert cmref.N == 2
assert cmshould.N == 3
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
Expand All @@ -339,6 +345,8 @@ def test_BoundaryNorm():
cmref.set_over('black')
cmshould = mcolors.ListedColormap(['blue', 'red', 'black'])

assert mcolors.same_color(cmref.get_over(), 'black')

assert cmref.N == 2
assert cmshould.N == 3
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
Expand Down Expand Up @@ -1160,4 +1168,15 @@ def test_repr_html():
cmap = plt.get_cmap('viridis')
html = cmap._repr_html_()
assert len(html) > 0
png = cmap._repr_png_()
assert base64.b64encode(png).decode('ascii') in html
assert cmap.name in html
assert html.startswith('<div')
assert html.endswith('</div>')


def test_get_under_over_bad():
cmap = plt.get_cmap('viridis')
assert_array_equal(cmap.get_under(), cmap(-np.inf))
assert_array_equal(cmap.get_over(), cmap(np.inf))
assert_array_equal(cmap.get_bad(), cmap(np.nan))