Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 13e3573

Browse filesBrowse 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 <2836374+timhoffm@users.noreply.github.com>
1 parent c7e4448 commit 13e3573
Copy full SHA for 13e3573

File tree

Expand file treeCollapse file tree

3 files changed

+81
-14
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+81
-14
lines changed
+7Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/colors.py
+55-14Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_colors.py
+19Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.