diff --git a/xarray/plot/dataarray_plot.py b/xarray/plot/dataarray_plot.py index 301cb5b501f..921f3dcae31 100644 --- a/xarray/plot/dataarray_plot.py +++ b/xarray/plot/dataarray_plot.py @@ -1594,7 +1594,7 @@ def newplotfunc( kwargs["levels"] = cmap_params["levels"] # if colors == a single color, matplotlib draws dashed negative # contours. we lose this feature if we pass cmap and not colors - if isinstance(colors, str): + if colors is not None: cmap_params["cmap"] = None kwargs["colors"] = colors diff --git a/xarray/plot/utils.py b/xarray/plot/utils.py index a71613562a5..a8aeb052ad6 100644 --- a/xarray/plot/utils.py +++ b/xarray/plot/utils.py @@ -931,9 +931,6 @@ def _process_cmap_cbar_kwargs( cbar_kwargs = {} if cbar_kwargs is None else dict(cbar_kwargs) - if "contour" in func.__name__ and levels is None: - levels = 7 # this is the matplotlib default - # colors is mutually exclusive with cmap if cmap and colors: raise ValueError("Can't specify both cmap and colors.") diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py index 3dd8a4fa689..b24423b5e34 100644 --- a/xarray/tests/test_plot.py +++ b/xarray/tests/test_plot.py @@ -1776,6 +1776,18 @@ def test_levels(self) -> None: artist = self.plotmethod(levels=3) assert artist.extend == "neither" + def test_colormap_norm(self) -> None: + # Using a norm should plot a nice colorbar and look consistent with pcolormesh. + norm = mpl.colors.LogNorm(0.1, 1e1) + + with pytest.warns(UserWarning): + artist = self.plotmethod(norm=norm, add_colorbar=True) + + actual = artist.colorbar.locator() + expected = np.array([0.01, 0.1, 1.0, 10.0]) + + np.testing.assert_allclose(actual, expected) + @pytest.mark.slow class TestContour(Common2dMixin, PlotTestCase): @@ -1792,16 +1804,18 @@ def test_colors(self) -> None: artist = self.plotmethod(colors="k") assert artist.cmap.colors[0] == "k" + # 2 colors, will repeat every other tick: artist = self.plotmethod(colors=["k", "b"]) - assert self._color_as_tuple(artist.cmap.colors[1]) == (0.0, 0.0, 1.0) + assert artist.cmap.colors[:2] == ["k", "b"] + # 4 colors, will repeat every 4th tick: artist = self.darray.plot.contour( levels=[-0.5, 0.0, 0.5, 1.0], colors=["k", "r", "w", "b"] ) - assert self._color_as_tuple(artist.cmap.colors[1]) == (1.0, 0.0, 0.0) - assert self._color_as_tuple(artist.cmap.colors[2]) == (1.0, 1.0, 1.0) + assert artist.cmap.colors[:5] == ["k", "r", "w", "b"] + # the last color is now under "over" - assert self._color_as_tuple(artist.cmap._rgba_over) == (0.0, 0.0, 1.0) + assert self._color_as_tuple(artist.cmap.get_over()) == (0.0, 0.0, 1.0) def test_colors_np_levels(self) -> None: # https://github.com/pydata/xarray/issues/3284 @@ -1809,15 +1823,11 @@ def test_colors_np_levels(self) -> None: artist = self.darray.plot.contour(levels=levels, colors=["k", "r", "w", "b"]) cmap = artist.cmap assert isinstance(cmap, mpl.colors.ListedColormap) - # non-optimal typing in matplotlib (ArrayLike) - # https://github.com/matplotlib/matplotlib/blob/84464dd085210fb57cc2419f0d4c0235391d97e6/lib/matplotlib/colors.pyi#L133 - colors = cast(np.ndarray, cmap.colors) - assert self._color_as_tuple(colors[1]) == (1.0, 0.0, 0.0) - assert self._color_as_tuple(colors[2]) == (1.0, 1.0, 1.0) + assert artist.cmap.colors[:5] == ["k", "r", "w", "b"] # type: ignore[attr-defined] + # the last color is now under "over" - assert hasattr(cmap, "_rgba_over") - assert self._color_as_tuple(cmap._rgba_over) == (0.0, 0.0, 1.0) + assert self._color_as_tuple(cmap.get_over()) == (0.0, 0.0, 1.0) def test_cmap_and_color_both(self) -> None: with pytest.raises(ValueError): @@ -1841,6 +1851,18 @@ def test_single_level(self) -> None: self.plotmethod(levels=[0.1]) self.plotmethod(levels=1) + def test_colormap_norm(self) -> None: + # Using a norm should plot a nice colorbar and look consistent with pcolormesh. + norm = mpl.colors.LogNorm(0.1, 1e1) + + with pytest.warns(UserWarning): + artist = self.plotmethod(norm=norm, add_colorbar=True) + + actual = artist.colorbar.locator() + expected = np.array([0.01, 0.1, 1.0, 10.0]) + + np.testing.assert_allclose(actual, expected) + class TestPcolormesh(Common2dMixin, PlotTestCase): plotfunc = staticmethod(xplt.pcolormesh)