Coverage for /usr/lib/python3/dist-packages/matplotlib/cm.py: 52%
217 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin.
4.. seealso::
6 :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps.
8 :doc:`/tutorials/colors/colormap-manipulation` for examples of how to
9 make colormaps.
11 :doc:`/tutorials/colors/colormaps` an in-depth discussion of
12 choosing colormaps.
14 :doc:`/tutorials/colors/colormapnorms` for more details about data
15 normalization.
16"""
18from collections.abc import Mapping
19import functools
21import numpy as np
22from numpy import ma
24import matplotlib as mpl
25from matplotlib import _api, colors, cbook, scale
26from matplotlib._cm import datad
27from matplotlib._cm_listed import cmaps as cmaps_listed
30@_api.caching_module_getattr # module-level deprecations
31class __getattr__:
32 LUTSIZE = _api.deprecated(
33 "3.5", obj_type="", alternative="rcParams['image.lut']")(
34 property(lambda self: _LUTSIZE))
37_LUTSIZE = mpl.rcParams['image.lut']
40def _gen_cmap_registry():
41 """
42 Generate a dict mapping standard colormap names to standard colormaps, as
43 well as the reversed colormaps.
44 """
45 cmap_d = {**cmaps_listed}
46 for name, spec in datad.items():
47 cmap_d[name] = ( # Precache the cmaps at a fixed lutsize..
48 colors.LinearSegmentedColormap(name, spec, _LUTSIZE)
49 if 'red' in spec else
50 colors.ListedColormap(spec['listed'], name)
51 if 'listed' in spec else
52 colors.LinearSegmentedColormap.from_list(name, spec, _LUTSIZE))
53 # Generate reversed cmaps.
54 for cmap in list(cmap_d.values()):
55 rmap = cmap.reversed()
56 cmap_d[rmap.name] = rmap
57 return cmap_d
60class ColormapRegistry(Mapping):
61 r"""
62 Container for colormaps that are known to Matplotlib by name.
64 The universal registry instance is `matplotlib.colormaps`. There should be
65 no need for users to instantiate `.ColormapRegistry` themselves.
67 Read access uses a dict-like interface mapping names to `.Colormap`\s::
69 import matplotlib as mpl
70 cmap = mpl.colormaps['viridis']
72 Returned `.Colormap`\s are copies, so that their modification does not
73 change the global definition of the colormap.
75 Additional colormaps can be added via `.ColormapRegistry.register`::
77 mpl.colormaps.register(my_colormap)
78 """
79 def __init__(self, cmaps):
80 self._cmaps = cmaps
81 self._builtin_cmaps = tuple(cmaps)
82 # A shim to allow register_cmap() to force an override
83 self._allow_override_builtin = False
85 def __getitem__(self, item):
86 try:
87 return self._cmaps[item].copy()
88 except KeyError:
89 raise KeyError(f"{item!r} is not a known colormap name") from None
91 def __iter__(self):
92 return iter(self._cmaps)
94 def __len__(self):
95 return len(self._cmaps)
97 def __str__(self):
98 return ('ColormapRegistry; available colormaps:\n' +
99 ', '.join(f"'{name}'" for name in self))
101 def __call__(self):
102 """
103 Return a list of the registered colormap names.
105 This exists only for backward-compatibility in `.pyplot` which had a
106 ``plt.colormaps()`` method. The recommended way to get this list is
107 now ``list(colormaps)``.
108 """
109 return list(self)
111 def register(self, cmap, *, name=None, force=False):
112 """
113 Register a new colormap.
115 The colormap name can then be used as a string argument to any ``cmap``
116 parameter in Matplotlib. It is also available in ``pyplot.get_cmap``.
118 The colormap registry stores a copy of the given colormap, so that
119 future changes to the original colormap instance do not affect the
120 registered colormap. Think of this as the registry taking a snapshot
121 of the colormap at registration.
123 Parameters
124 ----------
125 cmap : matplotlib.colors.Colormap
126 The colormap to register.
128 name : str, optional
129 The name for the colormap. If not given, ``cmap.name`` is used.
131 force : bool, default: False
132 If False, a ValueError is raised if trying to overwrite an already
133 registered name. True supports overwriting registered colormaps
134 other than the builtin colormaps.
135 """
136 _api.check_isinstance(colors.Colormap, cmap=cmap)
138 name = name or cmap.name
139 if name in self:
140 if not force:
141 # don't allow registering an already existing cmap
142 # unless explicitly asked to
143 raise ValueError(
144 f'A colormap named "{name}" is already registered.')
145 elif (name in self._builtin_cmaps
146 and not self._allow_override_builtin):
147 # We don't allow overriding a builtin unless privately
148 # coming from register_cmap()
149 raise ValueError("Re-registering the builtin cmap "
150 f"{name!r} is not allowed.")
152 # Warn that we are updating an already existing colormap
153 _api.warn_external(f"Overwriting the cmap {name!r} "
154 "that was already in the registry.")
156 self._cmaps[name] = cmap.copy()
158 def unregister(self, name):
159 """
160 Remove a colormap from the registry.
162 You cannot remove built-in colormaps.
164 If the named colormap is not registered, returns with no error, raises
165 if you try to de-register a default colormap.
167 .. warning::
169 Colormap names are currently a shared namespace that may be used
170 by multiple packages. Use `unregister` only if you know you
171 have registered that name before. In particular, do not
172 unregister just in case to clean the name before registering a
173 new colormap.
175 Parameters
176 ----------
177 name : str
178 The name of the colormap to be removed.
180 Raises
181 ------
182 ValueError
183 If you try to remove a default built-in colormap.
184 """
185 if name in self._builtin_cmaps:
186 raise ValueError(f"cannot unregister {name!r} which is a builtin "
187 "colormap.")
188 self._cmaps.pop(name, None)
190 def get_cmap(self, cmap):
191 """
192 Return a color map specified through *cmap*.
194 Parameters
195 ----------
196 cmap : str or `~matplotlib.colors.Colormap` or None
198 - if a `.Colormap`, return it
199 - if a string, look it up in ``mpl.colormaps``
200 - if None, return the Colormap defined in :rc:`image.cmap`
202 Returns
203 -------
204 Colormap
205 """
206 # get the default color map
207 if cmap is None:
208 return self[mpl.rcParams["image.cmap"]]
210 # if the user passed in a Colormap, simply return it
211 if isinstance(cmap, colors.Colormap):
212 return cmap
213 if isinstance(cmap, str):
214 _api.check_in_list(sorted(_colormaps), cmap=cmap)
215 # otherwise, it must be a string so look it up
216 return self[cmap]
217 raise TypeError(
218 'get_cmap expects None or an instance of a str or Colormap . ' +
219 f'you passed {cmap!r} of type {type(cmap)}'
220 )
223# public access to the colormaps should be via `matplotlib.colormaps`. For now,
224# we still create the registry here, but that should stay an implementation
225# detail.
226_colormaps = ColormapRegistry(_gen_cmap_registry())
227globals().update(_colormaps)
230@_api.deprecated(
231 '3.6',
232 pending=True,
233 alternative="``matplotlib.colormaps.register(name)``"
234)
235def register_cmap(name=None, cmap=None, *, override_builtin=False):
236 """
237 Add a colormap to the set recognized by :func:`get_cmap`.
239 Register a new colormap to be accessed by name ::
241 LinearSegmentedColormap('swirly', data, lut)
242 register_cmap(cmap=swirly_cmap)
244 Parameters
245 ----------
246 name : str, optional
247 The name that can be used in :func:`get_cmap` or :rc:`image.cmap`
249 If absent, the name will be the :attr:`~matplotlib.colors.Colormap.name`
250 attribute of the *cmap*.
252 cmap : matplotlib.colors.Colormap
253 Despite being the second argument and having a default value, this
254 is a required argument.
256 override_builtin : bool
258 Allow built-in colormaps to be overridden by a user-supplied
259 colormap.
261 Please do not use this unless you are sure you need it.
262 """
263 _api.check_isinstance((str, None), name=name)
264 if name is None:
265 try:
266 name = cmap.name
267 except AttributeError as err:
268 raise ValueError("Arguments must include a name or a "
269 "Colormap") from err
270 # override_builtin is allowed here for backward compatibility
271 # this is just a shim to enable that to work privately in
272 # the global ColormapRegistry
273 _colormaps._allow_override_builtin = override_builtin
274 _colormaps.register(cmap, name=name, force=override_builtin)
275 _colormaps._allow_override_builtin = False
278def _get_cmap(name=None, lut=None):
279 """
280 Get a colormap instance, defaulting to rc values if *name* is None.
282 Parameters
283 ----------
284 name : `matplotlib.colors.Colormap` or str or None, default: None
285 If a `.Colormap` instance, it will be returned. Otherwise, the name of
286 a colormap known to Matplotlib, which will be resampled by *lut*. The
287 default, None, means :rc:`image.cmap`.
288 lut : int or None, default: None
289 If *name* is not already a Colormap instance and *lut* is not None, the
290 colormap will be resampled to have *lut* entries in the lookup table.
292 Returns
293 -------
294 Colormap
295 """
296 if name is None:
297 name = mpl.rcParams['image.cmap']
298 if isinstance(name, colors.Colormap):
299 return name
300 _api.check_in_list(sorted(_colormaps), name=name)
301 if lut is None:
302 return _colormaps[name]
303 else:
304 return _colormaps[name].resampled(lut)
306# do it in two steps like this so we can have an un-deprecated version in
307# pyplot.
308get_cmap = _api.deprecated(
309 '3.6',
310 name='get_cmap',
311 pending=True,
312 alternative=(
313 "``matplotlib.colormaps[name]`` " +
314 "or ``matplotlib.colormaps.get_cmap(obj)``"
315 )
316)(_get_cmap)
319@_api.deprecated(
320 '3.6',
321 pending=True,
322 alternative="``matplotlib.colormaps.unregister(name)``"
323)
324def unregister_cmap(name):
325 """
326 Remove a colormap recognized by :func:`get_cmap`.
328 You may not remove built-in colormaps.
330 If the named colormap is not registered, returns with no error, raises
331 if you try to de-register a default colormap.
333 .. warning::
335 Colormap names are currently a shared namespace that may be used
336 by multiple packages. Use `unregister_cmap` only if you know you
337 have registered that name before. In particular, do not
338 unregister just in case to clean the name before registering a
339 new colormap.
341 Parameters
342 ----------
343 name : str
344 The name of the colormap to be un-registered
346 Returns
347 -------
348 ColorMap or None
349 If the colormap was registered, return it if not return `None`
351 Raises
352 ------
353 ValueError
354 If you try to de-register a default built-in colormap.
355 """
356 cmap = _colormaps.get(name, None)
357 _colormaps.unregister(name)
358 return cmap
361def _auto_norm_from_scale(scale_cls):
362 """
363 Automatically generate a norm class from *scale_cls*.
365 This differs from `.colors.make_norm_from_scale` in the following points:
367 - This function is not a class decorator, but directly returns a norm class
368 (as if decorating `.Normalize`).
369 - The scale is automatically constructed with ``nonpositive="mask"``, if it
370 supports such a parameter, to work around the difference in defaults
371 between standard scales (which use "clip") and norms (which use "mask").
373 Note that ``make_norm_from_scale`` caches the generated norm classes
374 (not the instances) and reuses them for later calls. For example,
375 ``type(_auto_norm_from_scale("log")) == LogNorm``.
376 """
377 # Actually try to construct an instance, to verify whether
378 # ``nonpositive="mask"`` is supported.
379 try:
380 norm = colors.make_norm_from_scale(
381 functools.partial(scale_cls, nonpositive="mask"))(
382 colors.Normalize)()
383 except TypeError:
384 norm = colors.make_norm_from_scale(scale_cls)(
385 colors.Normalize)()
386 return type(norm)
389class ScalarMappable:
390 """
391 A mixin class to map scalar data to RGBA.
393 The ScalarMappable applies data normalization before returning RGBA colors
394 from the given colormap.
395 """
397 def __init__(self, norm=None, cmap=None):
398 """
399 Parameters
400 ----------
401 norm : `.Normalize` (or subclass thereof) or str or None
402 The normalizing object which scales data, typically into the
403 interval ``[0, 1]``.
404 If a `str`, a `.Normalize` subclass is dynamically generated based
405 on the scale with the corresponding name.
406 If *None*, *norm* defaults to a *colors.Normalize* object which
407 initializes its scaling based on the first data processed.
408 cmap : str or `~matplotlib.colors.Colormap`
409 The colormap used to map normalized data values to RGBA colors.
410 """
411 self._A = None
412 self._norm = None # So that the setter knows we're initializing.
413 self.set_norm(norm) # The Normalize instance of this ScalarMappable.
414 self.cmap = None # So that the setter knows we're initializing.
415 self.set_cmap(cmap) # The Colormap instance of this ScalarMappable.
416 #: The last colorbar associated with this ScalarMappable. May be None.
417 self.colorbar = None
418 self.callbacks = cbook.CallbackRegistry(signals=["changed"])
420 callbacksSM = _api.deprecated("3.5", alternative="callbacks")(
421 property(lambda self: self.callbacks))
423 def _scale_norm(self, norm, vmin, vmax):
424 """
425 Helper for initial scaling.
427 Used by public functions that create a ScalarMappable and support
428 parameters *vmin*, *vmax* and *norm*. This makes sure that a *norm*
429 will take precedence over *vmin*, *vmax*.
431 Note that this method does not set the norm.
432 """
433 if vmin is not None or vmax is not None:
434 self.set_clim(vmin, vmax)
435 if isinstance(norm, colors.Normalize):
436 raise ValueError(
437 "Passing a Normalize instance simultaneously with "
438 "vmin/vmax is not supported. Please pass vmin/vmax "
439 "directly to the norm when creating it.")
441 # always resolve the autoscaling so we have concrete limits
442 # rather than deferring to draw time.
443 self.autoscale_None()
445 def to_rgba(self, x, alpha=None, bytes=False, norm=True):
446 """
447 Return a normalized rgba array corresponding to *x*.
449 In the normal case, *x* is a 1D or 2D sequence of scalars, and
450 the corresponding ndarray of rgba values will be returned,
451 based on the norm and colormap set for this ScalarMappable.
453 There is one special case, for handling images that are already
454 rgb or rgba, such as might have been read from an image file.
455 If *x* is an ndarray with 3 dimensions,
456 and the last dimension is either 3 or 4, then it will be
457 treated as an rgb or rgba array, and no mapping will be done.
458 The array can be uint8, or it can be floating point with
459 values in the 0-1 range; otherwise a ValueError will be raised.
460 If it is a masked array, the mask will be ignored.
461 If the last dimension is 3, the *alpha* kwarg (defaulting to 1)
462 will be used to fill in the transparency. If the last dimension
463 is 4, the *alpha* kwarg is ignored; it does not
464 replace the preexisting alpha. A ValueError will be raised
465 if the third dimension is other than 3 or 4.
467 In either case, if *bytes* is *False* (default), the rgba
468 array will be floats in the 0-1 range; if it is *True*,
469 the returned rgba array will be uint8 in the 0 to 255 range.
471 If norm is False, no normalization of the input data is
472 performed, and it is assumed to be in the range (0-1).
474 """
475 # First check for special case, image input:
476 try:
477 if x.ndim == 3:
478 if x.shape[2] == 3:
479 if alpha is None:
480 alpha = 1
481 if x.dtype == np.uint8:
482 alpha = np.uint8(alpha * 255)
483 m, n = x.shape[:2]
484 xx = np.empty(shape=(m, n, 4), dtype=x.dtype)
485 xx[:, :, :3] = x
486 xx[:, :, 3] = alpha
487 elif x.shape[2] == 4:
488 xx = x
489 else:
490 raise ValueError("Third dimension must be 3 or 4")
491 if xx.dtype.kind == 'f':
492 if norm and (xx.max() > 1 or xx.min() < 0):
493 raise ValueError("Floating point image RGB values "
494 "must be in the 0..1 range.")
495 if bytes:
496 xx = (xx * 255).astype(np.uint8)
497 elif xx.dtype == np.uint8:
498 if not bytes:
499 xx = xx.astype(np.float32) / 255
500 else:
501 raise ValueError("Image RGB array must be uint8 or "
502 "floating point; found %s" % xx.dtype)
503 return xx
504 except AttributeError:
505 # e.g., x is not an ndarray; so try mapping it
506 pass
508 # This is the normal case, mapping a scalar array:
509 x = ma.asarray(x)
510 if norm:
511 x = self.norm(x)
512 rgba = self.cmap(x, alpha=alpha, bytes=bytes)
513 return rgba
515 def set_array(self, A):
516 """
517 Set the value array from array-like *A*.
519 Parameters
520 ----------
521 A : array-like or None
522 The values that are mapped to colors.
524 The base class `.ScalarMappable` does not make any assumptions on
525 the dimensionality and shape of the value array *A*.
526 """
527 if A is None:
528 self._A = None
529 return
531 A = cbook.safe_masked_invalid(A, copy=True)
532 if not np.can_cast(A.dtype, float, "same_kind"):
533 raise TypeError(f"Image data of dtype {A.dtype} cannot be "
534 "converted to float")
536 self._A = A
538 def get_array(self):
539 """
540 Return the array of values, that are mapped to colors.
542 The base class `.ScalarMappable` does not make any assumptions on
543 the dimensionality and shape of the array.
544 """
545 return self._A
547 def get_cmap(self):
548 """Return the `.Colormap` instance."""
549 return self.cmap
551 def get_clim(self):
552 """
553 Return the values (min, max) that are mapped to the colormap limits.
554 """
555 return self.norm.vmin, self.norm.vmax
557 def set_clim(self, vmin=None, vmax=None):
558 """
559 Set the norm limits for image scaling.
561 Parameters
562 ----------
563 vmin, vmax : float
564 The limits.
566 The limits may also be passed as a tuple (*vmin*, *vmax*) as a
567 single positional argument.
569 .. ACCEPTS: (vmin: float, vmax: float)
570 """
571 # If the norm's limits are updated self.changed() will be called
572 # through the callbacks attached to the norm
573 if vmax is None:
574 try:
575 vmin, vmax = vmin
576 except (TypeError, ValueError):
577 pass
578 if vmin is not None:
579 self.norm.vmin = colors._sanitize_extrema(vmin)
580 if vmax is not None:
581 self.norm.vmax = colors._sanitize_extrema(vmax)
583 def get_alpha(self):
584 """
585 Returns
586 -------
587 float
588 Always returns 1.
589 """
590 # This method is intended to be overridden by Artist sub-classes
591 return 1.
593 def set_cmap(self, cmap):
594 """
595 Set the colormap for luminance data.
597 Parameters
598 ----------
599 cmap : `.Colormap` or str or None
600 """
601 in_init = self.cmap is None
603 self.cmap = _ensure_cmap(cmap)
604 if not in_init:
605 self.changed() # Things are not set up properly yet.
607 @property
608 def norm(self):
609 return self._norm
611 @norm.setter
612 def norm(self, norm):
613 _api.check_isinstance((colors.Normalize, str, None), norm=norm)
614 if norm is None:
615 norm = colors.Normalize()
616 elif isinstance(norm, str):
617 try:
618 scale_cls = scale._scale_mapping[norm]
619 except KeyError:
620 raise ValueError(
621 "Invalid norm str name; the following values are "
622 "supported: {}".format(", ".join(scale._scale_mapping))
623 ) from None
624 norm = _auto_norm_from_scale(scale_cls)()
626 if norm is self.norm:
627 # We aren't updating anything
628 return
630 in_init = self.norm is None
631 # Remove the current callback and connect to the new one
632 if not in_init:
633 self.norm.callbacks.disconnect(self._id_norm)
634 self._norm = norm
635 self._id_norm = self.norm.callbacks.connect('changed',
636 self.changed)
637 if not in_init:
638 self.changed()
640 def set_norm(self, norm):
641 """
642 Set the normalization instance.
644 Parameters
645 ----------
646 norm : `.Normalize` or str or None
648 Notes
649 -----
650 If there are any colorbars using the mappable for this norm, setting
651 the norm of the mappable will reset the norm, locator, and formatters
652 on the colorbar to default.
653 """
654 self.norm = norm
656 def autoscale(self):
657 """
658 Autoscale the scalar limits on the norm instance using the
659 current array
660 """
661 if self._A is None:
662 raise TypeError('You must first set_array for mappable')
663 # If the norm's limits are updated self.changed() will be called
664 # through the callbacks attached to the norm
665 self.norm.autoscale(self._A)
667 def autoscale_None(self):
668 """
669 Autoscale the scalar limits on the norm instance using the
670 current array, changing only limits that are None
671 """
672 if self._A is None:
673 raise TypeError('You must first set_array for mappable')
674 # If the norm's limits are updated self.changed() will be called
675 # through the callbacks attached to the norm
676 self.norm.autoscale_None(self._A)
678 def changed(self):
679 """
680 Call this whenever the mappable is changed to notify all the
681 callbackSM listeners to the 'changed' signal.
682 """
683 self.callbacks.process('changed', self)
684 self.stale = True
687# The docstrings here must be generic enough to apply to all relevant methods.
688mpl._docstring.interpd.update(
689 cmap_doc="""\
690cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
691 The Colormap instance or registered colormap name used to map scalar data
692 to colors.""",
693 norm_doc="""\
694norm : str or `~matplotlib.colors.Normalize`, optional
695 The normalization method used to scale scalar data to the [0, 1] range
696 before mapping to colors using *cmap*. By default, a linear scaling is
697 used, mapping the lowest value to 0 and the highest to 1.
699 If given, this can be one of the following:
701 - An instance of `.Normalize` or one of its subclasses
702 (see :doc:`/tutorials/colors/colormapnorms`).
703 - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a
704 list of available scales, call `matplotlib.scale.get_scale_names()`.
705 In that case, a suitable `.Normalize` subclass is dynamically generated
706 and instantiated.""",
707 vmin_vmax_doc="""\
708vmin, vmax : float, optional
709 When using scalar data and no explicit *norm*, *vmin* and *vmax* define
710 the data range that the colormap covers. By default, the colormap covers
711 the complete value range of the supplied data. It is an error to use
712 *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm*
713 name together with *vmin*/*vmax* is acceptable).""",
714)
717def _ensure_cmap(cmap):
718 """
719 Ensure that we have a `.Colormap` object.
721 For internal use to preserve type stability of errors.
723 Parameters
724 ----------
725 cmap : None, str, Colormap
727 - if a `Colormap`, return it
728 - if a string, look it up in mpl.colormaps
729 - if None, look up the default color map in mpl.colormaps
731 Returns
732 -------
733 Colormap
735 """
736 if isinstance(cmap, colors.Colormap):
737 return cmap
738 cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"]
739 # use check_in_list to ensure type stability of the exception raised by
740 # the internal usage of this (ValueError vs KeyError)
741 _api.check_in_list(sorted(_colormaps), cmap=cmap_name)
742 return mpl.colormaps[cmap_name]