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

Skip to content

Commit 13e3573

Browse files
bdicetimhoffm
andauthored
Add getters and _repr_html_ for over/under/bad values of Colormap objects. (#17900)
* Add get_bad, get_under, get_over. * Add missing docstring to is_gray. * Add under/over/bad colors to Colormap _repr_html_. Co-authored-by: Tim Hoffmann <[email protected]>
1 parent c7e4448 commit 13e3573

File tree

3 files changed

+81
-14
lines changed

3 files changed

+81
-14
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Get under/over/bad colors of Colormap objects
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
`matplotlib.colors.Colormap` now has methods
5+
`~.colors.Colormap.get_under`, `~.colors.Colormap.get_over`,
6+
`~.colors.Colormap.get_bad` for the colors used for out-of-range and masked
7+
values.

lib/matplotlib/colors.py

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ def __delitem__(self, key):
110110
_colors_full_map.update(BASE_COLORS)
111111
_colors_full_map = _ColorMapping(_colors_full_map)
112112

113+
_REPR_PNG_SIZE = (512, 64)
114+
113115

114116
def get_named_colors_mapping():
115117
"""Return the global mapping of names to named colors."""
@@ -607,26 +609,40 @@ def __copy__(self):
607609
cmapobject._global = False
608610
return cmapobject
609611

612+
def get_bad(self):
613+
"""Get the color for masked values."""
614+
if not self._isinit:
615+
self._init()
616+
return self._lut[self._i_bad]
617+
610618
def set_bad(self, color='k', alpha=None):
611619
"""Set the color for masked values."""
612620
_warn_if_global_cmap_modified(self)
613621
self._rgba_bad = to_rgba(color, alpha)
614622
if self._isinit:
615623
self._set_extremes()
616624

625+
def get_under(self):
626+
"""Get the color for low out-of-range values."""
627+
if not self._isinit:
628+
self._init()
629+
return self._lut[self._i_under]
630+
617631
def set_under(self, color='k', alpha=None):
618-
"""
619-
Set the color for low out-of-range values when ``norm.clip = False``.
620-
"""
632+
"""Set the color for low out-of-range values."""
621633
_warn_if_global_cmap_modified(self)
622634
self._rgba_under = to_rgba(color, alpha)
623635
if self._isinit:
624636
self._set_extremes()
625637

638+
def get_over(self):
639+
"""Get the color for high out-of-range values."""
640+
if not self._isinit:
641+
self._init()
642+
return self._lut[self._i_over]
643+
626644
def set_over(self, color='k', alpha=None):
627-
"""
628-
Set the color for high out-of-range values when ``norm.clip = False``.
629-
"""
645+
"""Set the color for high out-of-range values."""
630646
_warn_if_global_cmap_modified(self)
631647
self._rgba_over = to_rgba(color, alpha)
632648
if self._isinit:
@@ -648,6 +664,7 @@ def _init(self):
648664
raise NotImplementedError("Abstract class only")
649665

650666
def is_gray(self):
667+
"""Return whether the color map is grayscale."""
651668
if not self._isinit:
652669
self._init()
653670
return (np.all(self._lut[:, 0] == self._lut[:, 1]) and
@@ -678,8 +695,8 @@ def reversed(self, name=None):
678695

679696
def _repr_png_(self):
680697
"""Generate a PNG representation of the Colormap."""
681-
IMAGE_SIZE = (400, 50)
682-
X = np.tile(np.linspace(0, 1, IMAGE_SIZE[0]), (IMAGE_SIZE[1], 1))
698+
X = np.tile(np.linspace(0, 1, _REPR_PNG_SIZE[0]),
699+
(_REPR_PNG_SIZE[1], 1))
683700
pixels = self(X, bytes=True)
684701
png_bytes = io.BytesIO()
685702
title = self.name + ' color map'
@@ -696,12 +713,36 @@ def _repr_html_(self):
696713
"""Generate an HTML representation of the Colormap."""
697714
png_bytes = self._repr_png_()
698715
png_base64 = base64.b64encode(png_bytes).decode('ascii')
699-
return ('<strong>' + self.name + '</strong>' +
700-
'<img ' +
701-
'alt="' + self.name + ' color map" ' +
702-
'title="' + self.name + '"' +
703-
'style="border: 1px solid #555;" ' +
704-
'src="data:image/png;base64,' + png_base64 + '">')
716+
def color_block(color):
717+
hex_color = to_hex(color, keep_alpha=True)
718+
return (f'<div title="{hex_color}" '
719+
'style="display: inline-block; '
720+
'width: 1em; height: 1em; '
721+
'margin: 0; '
722+
'vertical-align: middle; '
723+
'border: 1px solid #555; '
724+
f'background-color: {hex_color};"></div>')
725+
726+
return ('<div style="vertical-align: middle;">'
727+
f'<strong>{self.name}</strong> '
728+
'</div>'
729+
'<div class="cmap"><img '
730+
f'alt="{self.name} color map" '
731+
f'title="{self.name}" '
732+
'style="border: 1px solid #555;" '
733+
f'src="data:image/png;base64,{png_base64}"></div>'
734+
'<div style="vertical-align: middle; '
735+
f'max-width: {_REPR_PNG_SIZE[0]+2}px; '
736+
'display: flex; justify-content: space-between;">'
737+
'<div style="float: left;">'
738+
f'{color_block(self.get_under())} under'
739+
'</div>'
740+
'<div style="margin: 0 auto; display: inline-block;">'
741+
f'bad {color_block(self.get_bad())}'
742+
'</div>'
743+
'<div style="float: right;">'
744+
f'over {color_block(self.get_over())}'
745+
'</div>')
705746

706747

707748
class LinearSegmentedColormap(Colormap):

lib/matplotlib/tests/test_colors.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66
from PIL import Image
77
import pytest
8+
import base64
89

910
from numpy.testing import assert_array_equal, assert_array_almost_equal
1011

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

307+
assert mcolors.same_color(cmref.get_over(), 'black')
308+
assert mcolors.same_color(cmref.get_under(), 'white')
309+
306310
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
307311
mynorm = mcolors.BoundaryNorm(bounds, cmshould.N, extend='both')
308312
assert mynorm.vmin == refnorm.vmin
@@ -323,6 +327,8 @@ def test_BoundaryNorm():
323327
cmref.set_under('white')
324328
cmshould = mcolors.ListedColormap(['white', 'blue', 'red'])
325329

330+
assert mcolors.same_color(cmref.get_under(), 'white')
331+
326332
assert cmref.N == 2
327333
assert cmshould.N == 3
328334
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
@@ -339,6 +345,8 @@ def test_BoundaryNorm():
339345
cmref.set_over('black')
340346
cmshould = mcolors.ListedColormap(['blue', 'red', 'black'])
341347

348+
assert mcolors.same_color(cmref.get_over(), 'black')
349+
342350
assert cmref.N == 2
343351
assert cmshould.N == 3
344352
refnorm = mcolors.BoundaryNorm(bounds, cmref.N)
@@ -1155,4 +1163,15 @@ def test_repr_html():
11551163
cmap = plt.get_cmap('viridis')
11561164
html = cmap._repr_html_()
11571165
assert len(html) > 0
1166+
png = cmap._repr_png_()
1167+
assert base64.b64encode(png).decode('ascii') in html
11581168
assert cmap.name in html
1169+
assert html.startswith('<div')
1170+
assert html.endswith('</div>')
1171+
1172+
1173+
def test_get_under_over_bad():
1174+
cmap = plt.get_cmap('viridis')
1175+
assert_array_equal(cmap.get_under(), cmap(-np.inf))
1176+
assert_array_equal(cmap.get_over(), cmap(np.inf))
1177+
assert_array_equal(cmap.get_bad(), cmap(np.nan))

0 commit comments

Comments
 (0)