Skip to content

Commit 9107078

Browse files
Use better contour defaults with logarithmic norms (#10565)
* Remove default re-definition * colors arg should take priority over default cmaps * Update test_plot.py * Update test_plot.py * add log norm test * Add tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test_plot.py * Update test_plot.py * Update test_plot.py * Update test_plot.py * Update whats-new.rst --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 37e512d commit 9107078

File tree

4 files changed

+36
-15
lines changed

4 files changed

+36
-15
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Deprecations
4343

4444
Bug fixes
4545
~~~~~~~~~
46+
- Fix contour plots not normalizing the colors correctly when using for example logarithmic norms. (:issue:`10551`, :pull:`10565`)
47+
By `Jimmy Westling <https://github.com/illviljan>`_.
4648
- Fix distribution of ``auto_complex`` keyword argument for open_datatree (:issue:`10631`, :pull:`10632`).
4749
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
4850
- Warn instead of raise in case of misconfiguration of ``unlimited_dims`` originating from dataset.encoding, to prevent breaking users workflows (:issue:`10647`, :pull:`10648`).

xarray/plot/dataarray_plot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,7 @@ def newplotfunc(
15941594
kwargs["levels"] = cmap_params["levels"]
15951595
# if colors == a single color, matplotlib draws dashed negative
15961596
# contours. we lose this feature if we pass cmap and not colors
1597-
if isinstance(colors, str):
1597+
if colors is not None:
15981598
cmap_params["cmap"] = None
15991599
kwargs["colors"] = colors
16001600

xarray/plot/utils.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -931,9 +931,6 @@ def _process_cmap_cbar_kwargs(
931931

932932
cbar_kwargs = {} if cbar_kwargs is None else dict(cbar_kwargs)
933933

934-
if "contour" in func.__name__ and levels is None:
935-
levels = 7 # this is the matplotlib default
936-
937934
# colors is mutually exclusive with cmap
938935
if cmap and colors:
939936
raise ValueError("Can't specify both cmap and colors.")

xarray/tests/test_plot.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,18 @@ def test_levels(self) -> None:
17761776
artist = self.plotmethod(levels=3)
17771777
assert artist.extend == "neither"
17781778

1779+
def test_colormap_norm(self) -> None:
1780+
# Using a norm should plot a nice colorbar and look consistent with pcolormesh.
1781+
norm = mpl.colors.LogNorm(0.1, 1e1)
1782+
1783+
with pytest.warns(UserWarning):
1784+
artist = self.plotmethod(norm=norm, add_colorbar=True)
1785+
1786+
actual = artist.colorbar.locator()
1787+
expected = np.array([0.01, 0.1, 1.0, 10.0])
1788+
1789+
np.testing.assert_allclose(actual, expected)
1790+
17791791

17801792
@pytest.mark.slow
17811793
class TestContour(Common2dMixin, PlotTestCase):
@@ -1792,32 +1804,30 @@ def test_colors(self) -> None:
17921804
artist = self.plotmethod(colors="k")
17931805
assert artist.cmap.colors[0] == "k"
17941806

1807+
# 2 colors, will repeat every other tick:
17951808
artist = self.plotmethod(colors=["k", "b"])
1796-
assert self._color_as_tuple(artist.cmap.colors[1]) == (0.0, 0.0, 1.0)
1809+
assert artist.cmap.colors[:2] == ["k", "b"]
17971810

1811+
# 4 colors, will repeat every 4th tick:
17981812
artist = self.darray.plot.contour(
17991813
levels=[-0.5, 0.0, 0.5, 1.0], colors=["k", "r", "w", "b"]
18001814
)
1801-
assert self._color_as_tuple(artist.cmap.colors[1]) == (1.0, 0.0, 0.0)
1802-
assert self._color_as_tuple(artist.cmap.colors[2]) == (1.0, 1.0, 1.0)
1815+
assert artist.cmap.colors[:5] == ["k", "r", "w", "b"]
1816+
18031817
# the last color is now under "over"
1804-
assert self._color_as_tuple(artist.cmap._rgba_over) == (0.0, 0.0, 1.0)
1818+
assert self._color_as_tuple(artist.cmap.get_over()) == (0.0, 0.0, 1.0)
18051819

18061820
def test_colors_np_levels(self) -> None:
18071821
# https://github.com/pydata/xarray/issues/3284
18081822
levels = np.array([-0.5, 0.0, 0.5, 1.0])
18091823
artist = self.darray.plot.contour(levels=levels, colors=["k", "r", "w", "b"])
18101824
cmap = artist.cmap
18111825
assert isinstance(cmap, mpl.colors.ListedColormap)
1812-
# non-optimal typing in matplotlib (ArrayLike)
1813-
# https://github.com/matplotlib/matplotlib/blob/84464dd085210fb57cc2419f0d4c0235391d97e6/lib/matplotlib/colors.pyi#L133
1814-
colors = cast(np.ndarray, cmap.colors)
18151826

1816-
assert self._color_as_tuple(colors[1]) == (1.0, 0.0, 0.0)
1817-
assert self._color_as_tuple(colors[2]) == (1.0, 1.0, 1.0)
1827+
assert artist.cmap.colors[:5] == ["k", "r", "w", "b"] # type: ignore[attr-defined]
1828+
18181829
# the last color is now under "over"
1819-
assert hasattr(cmap, "_rgba_over")
1820-
assert self._color_as_tuple(cmap._rgba_over) == (0.0, 0.0, 1.0)
1830+
assert self._color_as_tuple(cmap.get_over()) == (0.0, 0.0, 1.0)
18211831

18221832
def test_cmap_and_color_both(self) -> None:
18231833
with pytest.raises(ValueError):
@@ -1841,6 +1851,18 @@ def test_single_level(self) -> None:
18411851
self.plotmethod(levels=[0.1])
18421852
self.plotmethod(levels=1)
18431853

1854+
def test_colormap_norm(self) -> None:
1855+
# Using a norm should plot a nice colorbar and look consistent with pcolormesh.
1856+
norm = mpl.colors.LogNorm(0.1, 1e1)
1857+
1858+
with pytest.warns(UserWarning):
1859+
artist = self.plotmethod(norm=norm, add_colorbar=True)
1860+
1861+
actual = artist.colorbar.locator()
1862+
expected = np.array([0.01, 0.1, 1.0, 10.0])
1863+
1864+
np.testing.assert_allclose(actual, expected)
1865+
18441866

18451867
class TestPcolormesh(Common2dMixin, PlotTestCase):
18461868
plotfunc = staticmethod(xplt.pcolormesh)

0 commit comments

Comments
 (0)