cm.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. """
  2. Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin.
  3. .. seealso::
  4. :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps.
  5. :ref:`colormap-manipulation` for examples of how to make
  6. colormaps.
  7. :ref:`colormaps` an in-depth discussion of choosing
  8. colormaps.
  9. :ref:`colormapnorms` for more details about data normalization.
  10. """
  11. from collections.abc import Mapping
  12. import matplotlib as mpl
  13. from matplotlib import _api, colors
  14. # TODO make this warn on access
  15. from matplotlib.colorizer import _ScalarMappable as ScalarMappable # noqa
  16. from matplotlib._cm import datad
  17. from matplotlib._cm_listed import cmaps as cmaps_listed
  18. from matplotlib._cm_multivar import cmap_families as multivar_cmaps
  19. from matplotlib._cm_bivar import cmaps as bivar_cmaps
  20. _LUTSIZE = mpl.rcParams['image.lut']
  21. def _gen_cmap_registry():
  22. """
  23. Generate a dict mapping standard colormap names to standard colormaps, as
  24. well as the reversed colormaps.
  25. """
  26. cmap_d = {**cmaps_listed}
  27. for name, spec in datad.items():
  28. cmap_d[name] = ( # Precache the cmaps at a fixed lutsize..
  29. colors.LinearSegmentedColormap(name, spec, _LUTSIZE)
  30. if 'red' in spec else
  31. colors.ListedColormap(spec['listed'], name)
  32. if 'listed' in spec else
  33. colors.LinearSegmentedColormap.from_list(name, spec, _LUTSIZE))
  34. # Register colormap aliases for gray and grey.
  35. aliases = {
  36. # alias -> original name
  37. 'grey': 'gray',
  38. 'gist_grey': 'gist_gray',
  39. 'gist_yerg': 'gist_yarg',
  40. 'Grays': 'Greys',
  41. }
  42. for alias, original_name in aliases.items():
  43. cmap = cmap_d[original_name].copy()
  44. cmap.name = alias
  45. cmap_d[alias] = cmap
  46. # Generate reversed cmaps.
  47. for cmap in list(cmap_d.values()):
  48. rmap = cmap.reversed()
  49. cmap_d[rmap.name] = rmap
  50. return cmap_d
  51. class ColormapRegistry(Mapping):
  52. r"""
  53. Container for colormaps that are known to Matplotlib by name.
  54. The universal registry instance is `matplotlib.colormaps`. There should be
  55. no need for users to instantiate `.ColormapRegistry` themselves.
  56. Read access uses a dict-like interface mapping names to `.Colormap`\s::
  57. import matplotlib as mpl
  58. cmap = mpl.colormaps['viridis']
  59. Returned `.Colormap`\s are copies, so that their modification does not
  60. change the global definition of the colormap.
  61. Additional colormaps can be added via `.ColormapRegistry.register`::
  62. mpl.colormaps.register(my_colormap)
  63. To get a list of all registered colormaps, you can do::
  64. from matplotlib import colormaps
  65. list(colormaps)
  66. """
  67. def __init__(self, cmaps):
  68. self._cmaps = cmaps
  69. self._builtin_cmaps = tuple(cmaps)
  70. def __getitem__(self, item):
  71. try:
  72. return self._cmaps[item].copy()
  73. except KeyError:
  74. raise KeyError(f"{item!r} is not a known colormap name") from None
  75. def __iter__(self):
  76. return iter(self._cmaps)
  77. def __len__(self):
  78. return len(self._cmaps)
  79. def __str__(self):
  80. return ('ColormapRegistry; available colormaps:\n' +
  81. ', '.join(f"'{name}'" for name in self))
  82. def __call__(self):
  83. """
  84. Return a list of the registered colormap names.
  85. This exists only for backward-compatibility in `.pyplot` which had a
  86. ``plt.colormaps()`` method. The recommended way to get this list is
  87. now ``list(colormaps)``.
  88. """
  89. return list(self)
  90. def register(self, cmap, *, name=None, force=False):
  91. """
  92. Register a new colormap.
  93. The colormap name can then be used as a string argument to any ``cmap``
  94. parameter in Matplotlib. It is also available in ``pyplot.get_cmap``.
  95. The colormap registry stores a copy of the given colormap, so that
  96. future changes to the original colormap instance do not affect the
  97. registered colormap. Think of this as the registry taking a snapshot
  98. of the colormap at registration.
  99. Parameters
  100. ----------
  101. cmap : matplotlib.colors.Colormap
  102. The colormap to register.
  103. name : str, optional
  104. The name for the colormap. If not given, ``cmap.name`` is used.
  105. force : bool, default: False
  106. If False, a ValueError is raised if trying to overwrite an already
  107. registered name. True supports overwriting registered colormaps
  108. other than the builtin colormaps.
  109. """
  110. _api.check_isinstance(colors.Colormap, cmap=cmap)
  111. name = name or cmap.name
  112. if name in self:
  113. if not force:
  114. # don't allow registering an already existing cmap
  115. # unless explicitly asked to
  116. raise ValueError(
  117. f'A colormap named "{name}" is already registered.')
  118. elif name in self._builtin_cmaps:
  119. # We don't allow overriding a builtin.
  120. raise ValueError("Re-registering the builtin cmap "
  121. f"{name!r} is not allowed.")
  122. # Warn that we are updating an already existing colormap
  123. _api.warn_external(f"Overwriting the cmap {name!r} "
  124. "that was already in the registry.")
  125. self._cmaps[name] = cmap.copy()
  126. # Someone may set the extremes of a builtin colormap and want to register it
  127. # with a different name for future lookups. The object would still have the
  128. # builtin name, so we should update it to the registered name
  129. if self._cmaps[name].name != name:
  130. self._cmaps[name].name = name
  131. def unregister(self, name):
  132. """
  133. Remove a colormap from the registry.
  134. You cannot remove built-in colormaps.
  135. If the named colormap is not registered, returns with no error, raises
  136. if you try to de-register a default colormap.
  137. .. warning::
  138. Colormap names are currently a shared namespace that may be used
  139. by multiple packages. Use `unregister` only if you know you
  140. have registered that name before. In particular, do not
  141. unregister just in case to clean the name before registering a
  142. new colormap.
  143. Parameters
  144. ----------
  145. name : str
  146. The name of the colormap to be removed.
  147. Raises
  148. ------
  149. ValueError
  150. If you try to remove a default built-in colormap.
  151. """
  152. if name in self._builtin_cmaps:
  153. raise ValueError(f"cannot unregister {name!r} which is a builtin "
  154. "colormap.")
  155. self._cmaps.pop(name, None)
  156. def get_cmap(self, cmap):
  157. """
  158. Return a color map specified through *cmap*.
  159. Parameters
  160. ----------
  161. cmap : str or `~matplotlib.colors.Colormap` or None
  162. - if a `.Colormap`, return it
  163. - if a string, look it up in ``mpl.colormaps``
  164. - if None, return the Colormap defined in :rc:`image.cmap`
  165. Returns
  166. -------
  167. Colormap
  168. """
  169. # get the default color map
  170. if cmap is None:
  171. return self[mpl.rcParams["image.cmap"]]
  172. # if the user passed in a Colormap, simply return it
  173. if isinstance(cmap, colors.Colormap):
  174. return cmap
  175. if isinstance(cmap, str):
  176. _api.check_in_list(sorted(_colormaps), cmap=cmap)
  177. # otherwise, it must be a string so look it up
  178. return self[cmap]
  179. raise TypeError(
  180. 'get_cmap expects None or an instance of a str or Colormap . ' +
  181. f'you passed {cmap!r} of type {type(cmap)}'
  182. )
  183. # public access to the colormaps should be via `matplotlib.colormaps`. For now,
  184. # we still create the registry here, but that should stay an implementation
  185. # detail.
  186. _colormaps = ColormapRegistry(_gen_cmap_registry())
  187. globals().update(_colormaps)
  188. _multivar_colormaps = ColormapRegistry(multivar_cmaps)
  189. _bivar_colormaps = ColormapRegistry(bivar_cmaps)
  190. # This is an exact copy of pyplot.get_cmap(). It was removed in 3.9, but apparently
  191. # caused more user trouble than expected. Re-added for 3.9.1 and extended the
  192. # deprecation period for two additional minor releases.
  193. @_api.deprecated(
  194. '3.7',
  195. removal='3.11',
  196. alternative="``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()``"
  197. " or ``pyplot.get_cmap()``"
  198. )
  199. def get_cmap(name=None, lut=None):
  200. """
  201. Get a colormap instance, defaulting to rc values if *name* is None.
  202. Parameters
  203. ----------
  204. name : `~matplotlib.colors.Colormap` or str or None, default: None
  205. If a `.Colormap` instance, it will be returned. Otherwise, the name of
  206. a colormap known to Matplotlib, which will be resampled by *lut*. The
  207. default, None, means :rc:`image.cmap`.
  208. lut : int or None, default: None
  209. If *name* is not already a Colormap instance and *lut* is not None, the
  210. colormap will be resampled to have *lut* entries in the lookup table.
  211. Returns
  212. -------
  213. Colormap
  214. """
  215. if name is None:
  216. name = mpl.rcParams['image.cmap']
  217. if isinstance(name, colors.Colormap):
  218. return name
  219. _api.check_in_list(sorted(_colormaps), name=name)
  220. if lut is None:
  221. return _colormaps[name]
  222. else:
  223. return _colormaps[name].resampled(lut)
  224. def _ensure_cmap(cmap):
  225. """
  226. Ensure that we have a `.Colormap` object.
  227. For internal use to preserve type stability of errors.
  228. Parameters
  229. ----------
  230. cmap : None, str, Colormap
  231. - if a `Colormap`, return it
  232. - if a string, look it up in mpl.colormaps
  233. - if None, look up the default color map in mpl.colormaps
  234. Returns
  235. -------
  236. Colormap
  237. """
  238. if isinstance(cmap, colors.Colormap):
  239. return cmap
  240. cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"]
  241. # use check_in_list to ensure type stability of the exception raised by
  242. # the internal usage of this (ValueError vs KeyError)
  243. if cmap_name not in _colormaps:
  244. _api.check_in_list(sorted(_colormaps), cmap=cmap_name)
  245. return mpl.colormaps[cmap_name]