Skip to content

Deprecate QuadContourSet.allsegs, .allkinds, .tcolors, .tlinewidths. #25138

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 1 commit into from
Mar 10, 2023
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
5 changes: 5 additions & 0 deletions doc/api/next_api_changes/deprecations/25138-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``allsegs``, ``allkinds``, ``tcolors`` and ``tlinewidths`` attributes of `.ContourSet`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These attributes are deprecated; if required, directly retrieve the vertices
and codes of the Path objects in ``QuadContourSet.collections`` and the colors
and the linewidths of these collections.
4 changes: 2 additions & 2 deletions lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,8 +774,8 @@ def add_lines(self, *args, **kwargs):
# TODO: Make colorbar lines auto-follow changes in contour lines.
return self.add_lines(
CS.levels,
[c[0] for c in CS.tcolors],
[t[0] for t in CS.tlinewidths],
CS.to_rgba(CS.cvalues, CS.alpha),
[coll.get_linewidths()[0] for coll in CS.collections],
erase=erase)
else:
self, levels, colors, linewidths, erase = params.values()
Expand Down
76 changes: 40 additions & 36 deletions lib/matplotlib/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

import functools
import itertools
from numbers import Integral

import numpy as np
Expand Down Expand Up @@ -767,6 +766,10 @@ def __init__(self, ax, *args,
self.negative_linestyles = \
mpl.rcParams['contour.negative_linestyle']

# The base class _process_args will update _allpaths, which gets picked
# up by _get_allpaths below. OTOH the _process_args of subclasses
# leave _allpaths as None and instead set _contour_generator.
self._allpaths = None
kwargs = self._process_args(*args, **kwargs)
self._process_levels()

Expand Down Expand Up @@ -820,23 +823,7 @@ def __init__(self, ax, *args,
self.norm.vmax = vmax
self._process_colors()

if getattr(self, 'allsegs', None) is None:
self.allsegs, self.allkinds = self._get_allsegs_and_allkinds()
elif self.allkinds is None:
# allsegs specified in constructor may or may not have allkinds as
# well. Must ensure allkinds can be zipped below.
self.allkinds = [None] * len(self.allsegs)

# Each entry in (allsegs, allkinds) is a list of (segs, kinds) which
# specifies a list of Paths: segs is a list of (N, 2) arrays of xy
# coordinates, kinds is a list of arrays of corresponding pathcodes.
# However, kinds can also be None; in which case all paths in that list
# are codeless.
allpaths = [
[*map(mpath.Path,
segs,
kinds if kinds is not None else itertools.repeat(None))]
for segs, kinds in zip(self.allsegs, self.allkinds)]
allpaths = self._get_allpaths()

if self.filled:
if self.linewidths is not None:
Expand All @@ -857,7 +844,7 @@ def __init__(self, ax, *args,
for level, level_upper, paths
in zip(lowers, uppers, allpaths)]
else:
self.tlinewidths = tlinewidths = self._process_linewidths()
tlinewidths = self._process_linewidths()
tlinestyles = self._process_linestyles()
aa = self.antialiased
if aa is not None:
Expand Down Expand Up @@ -895,6 +882,15 @@ def __init__(self, ax, *args,
", ".join(map(repr, kwargs))
)

allsegs = _api.deprecated("3.8", pending=True)(property(lambda self: [
p.vertices for c in self.collections for p in c.get_paths()]))
allkinds = _api.deprecated("3.8", pending=True)(property(lambda self: [
p.codes for c in self.collections for p in c.get_paths()]))
tcolors = _api.deprecated("3.8")(property(lambda self: [
(tuple(rgba),) for rgba in self.to_rgba(self.cvalues, self.alpha)]))
tlinewidths = _api.deprecated("3.8")(
property(lambda self: self._process_linewidths()))

def get_transform(self):
"""Return the `.Transform` instance used by this ContourSet."""
if self._transform is None:
Expand Down Expand Up @@ -979,51 +975,60 @@ def _process_args(self, *args, **kwargs):
Must set self.levels, self.zmin and self.zmax, and update axes limits.
"""
self.levels = args[0]
self.allsegs = args[1]
self.allkinds = args[2] if len(args) > 2 else None
allsegs = args[1]
allkinds = args[2] if len(args) > 2 else None
self.zmax = np.max(self.levels)
self.zmin = np.min(self.levels)

if allkinds is None:
allkinds = [[None] * len(segs) for segs in allsegs]

# Check lengths of levels and allsegs.
if self.filled:
if len(self.allsegs) != len(self.levels) - 1:
if len(allsegs) != len(self.levels) - 1:
raise ValueError('must be one less number of segments as '
'levels')
else:
if len(self.allsegs) != len(self.levels):
if len(allsegs) != len(self.levels):
raise ValueError('must be same number of segments as levels')

# Check length of allkinds.
if (self.allkinds is not None and
len(self.allkinds) != len(self.allsegs)):
if len(allkinds) != len(allsegs):
raise ValueError('allkinds has different length to allsegs')

# Determine x, y bounds and update axes data limits.
flatseglist = [s for seg in self.allsegs for s in seg]
flatseglist = [s for seg in allsegs for s in seg]
points = np.concatenate(flatseglist, axis=0)
self._mins = points.min(axis=0)
self._maxs = points.max(axis=0)

# Each entry in (allsegs, allkinds) is a list of (segs, kinds) which
# specifies a list of Paths: segs is a list of (N, 2) arrays of xy
# coordinates, kinds is a list of arrays of corresponding pathcodes.
# However, kinds can also be None; in which case all paths in that list
# are codeless (this case is normalized above).
self._allpaths = [[*map(mpath.Path, segs, kinds)]
for segs, kinds in zip(allsegs, allkinds)]

return kwargs

def _get_allsegs_and_allkinds(self):
"""Compute ``allsegs`` and ``allkinds`` using C extension."""
allsegs = []
allkinds = []
def _get_allpaths(self):
"""Compute ``allpaths`` using C extension."""
if self._allpaths is not None:
return self._allpaths
allpaths = []
if self.filled:
lowers, uppers = self._get_lowers_and_uppers()
for level, level_upper in zip(lowers, uppers):
vertices, kinds = \
self._contour_generator.create_filled_contour(
level, level_upper)
allsegs.append(vertices)
allkinds.append(kinds)
allpaths.append([*map(mpath.Path, vertices, kinds)])
else:
for level in self.levels:
vertices, kinds = self._contour_generator.create_contour(level)
allsegs.append(vertices)
allkinds.append(kinds)
return allsegs, allkinds
allpaths.append([*map(mpath.Path, vertices, kinds)])
return allpaths

def _get_lowers_and_uppers(self):
"""
Expand Down Expand Up @@ -1052,7 +1057,6 @@ def changed(self):
self.norm.autoscale_None(self.levels)
tcolors = [(tuple(rgba),)
for rgba in self.to_rgba(self.cvalues, alpha=self.alpha)]
self.tcolors = tcolors
hatches = self.hatches * len(tcolors)
for color, hatch, collection in zip(tcolors, hatches,
self.collections):
Expand Down
22 changes: 19 additions & 3 deletions lib/matplotlib/tests/test_contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import contourpy
import numpy as np
from numpy.testing import (
assert_array_almost_equal, assert_array_almost_equal_nulp)
assert_array_almost_equal, assert_array_almost_equal_nulp, assert_array_equal)
import matplotlib as mpl
from matplotlib.testing.decorators import image_comparison
from matplotlib import pyplot as plt, rc_context, ticker
from matplotlib._api import MatplotlibDeprecationWarning
from matplotlib.colors import LogNorm, same_color
from matplotlib.testing.decorators import image_comparison
import pytest


Expand Down Expand Up @@ -365,7 +366,9 @@ def test_contour_linewidth(
fig, ax = plt.subplots()
X = np.arange(4*3).reshape(4, 3)
cs = ax.contour(X, linewidths=call_linewidths)
assert cs.tlinewidths[0][0] == expected
assert cs.collections[0].get_linewidths()[0] == expected
with pytest.warns(MatplotlibDeprecationWarning, match="tlinewidths"):
assert cs.tlinewidths[0][0] == expected


@pytest.mark.backend("pdf")
Expand Down Expand Up @@ -722,3 +725,16 @@ def test_all_nan():
assert_array_almost_equal(plt.contour(x).levels,
[-1e-13, -7.5e-14, -5e-14, -2.4e-14, 0.0,
2.4e-14, 5e-14, 7.5e-14, 1e-13])


def test_deprecated_apis():
cs = plt.contour(np.arange(16).reshape((4, 4)))
colls = cs.collections
with pytest.warns(PendingDeprecationWarning, match="allsegs"):
assert cs.allsegs == [p.vertices for c in colls for p in c.get_paths()]
with pytest.warns(PendingDeprecationWarning, match="allkinds"):
assert cs.allkinds == [p.codes for c in colls for p in c.get_paths()]
with pytest.warns(MatplotlibDeprecationWarning, match="tcolors"):
assert_array_equal(cs.tcolors, [c.get_edgecolor() for c in colls])
with pytest.warns(MatplotlibDeprecationWarning, match="tlinewidths"):
assert cs.tlinewidths == [c.get_linewidth() for c in colls]