Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- Consolidate package metadata and configuration in *pyproject.toml* ([#165](https://github.com/mpytools/mplotutils/pull/165)).
- Replace deprecated `matplotlib.rcsetup.all_backends` with `matplotlib.backends.backend_registry.list_builtin()`
([#160](https://github.com/mpytools/mplotutils/pull/160)).
- Converted the hatch tests to a class based approach ([#146](https://github.com/mpytools/mplotutils/issues/146)).
- Also upload coverage report in upstream dev CI ([#162](https://github.com/mpytools/mplotutils/pull/162)).

## v0.6.0 (04.12.2024)
Expand Down
280 changes: 134 additions & 146 deletions mplotutils/tests/test_hatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,225 +14,213 @@
requires_mpl_ge_310 = pytest.mark.skipif(not MPL_GE_310, reason="requires mpl >= 3.10")


HATCH_FUNCTIONS = (
pytest.param(mpu.hatch, id="hatch"),
pytest.param(mpu.hatch_map, id="hatch_map"),
pytest.param(mpu.hatch_map_global, id="hatch_map_global"),
)


def get_hatchcolor(h):
if MPL_GE_311:
return mpl.colors.to_rgba(h.get_hatchcolor())

return h._hatch_color


@pytest.mark.parametrize("obj", (None, xr.Dataset(), np.array([])))
@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_not_a_dataarray(obj, function):
class HatchBase:

with pytest.raises(TypeError, match="Expected a xr.DataArray"):
function(obj, "*")
@pytest.mark.parametrize("obj", (None, xr.Dataset(), np.array([])))
def test_hatch_not_a_dataarray(self, obj):

with pytest.raises(TypeError, match="Expected a xr.DataArray"):
self.function(obj, "*")

@pytest.mark.parametrize("dtype", (float, int))
@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_not_bool(dtype, function):
@pytest.mark.parametrize("dtype", (float, int))
def test_hatch_not_bool(self, dtype):

da = xr.DataArray(np.ones((3, 3), dtype=dtype))
da = xr.DataArray(np.ones((3, 3), dtype=dtype))

with pytest.raises(TypeError, match="Expected a boolean array"):
function(da, "*")
with pytest.raises(TypeError, match="Expected a boolean array"):
self.function(da, "*")

@pytest.mark.parametrize("ndim", (1, 3))
def test_hatch_not_2D(self, ndim):

@pytest.mark.parametrize("ndim", (1, 3))
@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_not_2D(ndim, function):
da = xr.DataArray(np.ones([3] * ndim, dtype=bool))

da = xr.DataArray(np.ones([3] * ndim, dtype=bool))
with pytest.raises(ValueError, match="Expected a 2D array"):
self.function(da, "*")

with pytest.raises(ValueError, match="Expected a 2D array"):
function(da, "*")
def test_hatch_pattern(self):

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)

@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_pattern(function):
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)
h = self.function(da, "*", ax=ax)
assert h.hatches == ["", "*"]
h = self.function(da, "//", ax=ax)
assert h.hatches == ["", "//"]

subplot_kw = {"projection": ccrs.PlateCarree()}
def test_hatch_label(self):

with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)

h = function(da, "*", ax=ax)
assert h.hatches == ["", "*"]
h = function(da, "//", ax=ax)
assert h.hatches == ["", "//"]
# test label with default color
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

self.function(da, "*", ax=ax, label="label")

@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_label(function):
legend = ax.legend()
h = legend.legend_handles

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)
assert len(h) == 1

subplot_kw = {"projection": ccrs.PlateCarree()}
(rect,) = h

# test label with default color
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
assert rect.get_label() == "label"
assert mpl.colors.to_rgba("0.1") == get_hatchcolor(rect)

function(da, "*", ax=ax, label="label")
# test 2 labels with non-default color
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

legend = ax.legend()
h = legend.legend_handles
self.function(da, "*", ax=ax, label="label0", color="#2ca25f")
self.function(da, "*", ax=ax, label="label1", color="#2ca25f")

assert len(h) == 1
legend = ax.legend()
h = legend.legend_handles

(rect,) = h
assert len(h) == 2

assert rect.get_label() == "label"
assert mpl.colors.to_rgba("0.1") == get_hatchcolor(rect)
for i, rect in enumerate(h):
assert rect.get_label() == f"label{i}"
assert mpl.colors.to_rgba("#2ca25f") == get_hatchcolor(rect)

# test 2 labels with non-default color
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
@pytest.mark.skipif(MPL_GE_310, reason="only for mpl < 3.10")
def test_hatch_linewidth_mpl_lt_310(self):

function(da, "*", ax=ax, label="label0", color="#2ca25f")
function(da, "*", ax=ax, label="label1", color="#2ca25f")
da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)

legend = ax.legend()
h = legend.legend_handles
# test linewidth default width
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):
self.function(da, "*", ax=ax)

assert len(h) == 2
assert mpl.rcParams["hatch.linewidth"] == 0.25

for i, rect in enumerate(h):
assert rect.get_label() == f"label{i}"
assert mpl.colors.to_rgba("#2ca25f") == get_hatchcolor(rect)
# changing away from the default linewidth does not raise a warning
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

self.function(da, "*", ax=ax)
assert mpl.rcParams["hatch.linewidth"] == 0.25

@pytest.mark.skipif(MPL_GE_310, reason="only for mpl < 3.10")
@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_linewidth_mpl_lt_310(function):
with assert_no_warnings():
self.function(da, "*", ax=ax, linewidth=1)

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)
assert mpl.rcParams["hatch.linewidth"] == 1

subplot_kw = {"projection": ccrs.PlateCarree()}
# changing away from the linewidth does raise a warning
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

# test linewidth default width
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
function(da, "*", ax=ax)
self.function(da, "*", ax=ax, linewidth=2)
assert mpl.rcParams["hatch.linewidth"] == 2

assert mpl.rcParams["hatch.linewidth"] == 0.25
with pytest.warns(match="Setting more than one hatch `linewidth`"):
self.function(da, "*", ax=ax, linewidth=1)

# changing away from the default linewidth does not raise a warning
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
assert mpl.rcParams["hatch.linewidth"] == 1

function(da, "*", ax=ax)
assert mpl.rcParams["hatch.linewidth"] == 0.25
@requires_mpl_ge_310
@pytest.mark.filterwarnings("ignore:Passing 'N' to ListedColormap is deprecated")
def test_hatch_linewidth_mpl_ge_310(self):

with assert_no_warnings():
function(da, "*", ax=ax, linewidth=1)
da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)

assert mpl.rcParams["hatch.linewidth"] == 1
# test linewidth default width
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):
q = self.function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25

# changing away from the linewidth does raise a warning
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
# changing away from the default linewidth does not raise a warning
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

function(da, "*", ax=ax, linewidth=2)
assert mpl.rcParams["hatch.linewidth"] == 2
q = self.function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25

with pytest.warns(match="Setting more than one hatch `linewidth`"):
function(da, "*", ax=ax, linewidth=1)
with assert_no_warnings():
q = self.function(da, "*", ax=ax, linewidth=1)

assert mpl.rcParams["hatch.linewidth"] == 1
assert q.get_hatch_linewidth() == 1
assert mpl.rcParams["hatch.linewidth"] == 1

q = self.function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25

@requires_mpl_ge_310
@pytest.mark.filterwarnings("ignore:Passing 'N' to ListedColormap is deprecated")
@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_linewidth_mpl_ge_310(function):
# changing away from the linewidth does NOT raise a warning
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)
q = self.function(da, "*", ax=ax, linewidth=2)
assert q.get_hatch_linewidth() == 2
assert mpl.rcParams["hatch.linewidth"] == 2

subplot_kw = {"projection": ccrs.PlateCarree()}
with assert_no_warnings():
q = self.function(da, "*", ax=ax, linewidth=1)

# test linewidth default width
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
q = function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25
assert q.get_hatch_linewidth() == 1
assert mpl.rcParams["hatch.linewidth"] == 1

# changing away from the default linewidth does not raise a warning
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
def test_hatch_color(self):

q = function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25
da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)

with assert_no_warnings():
q = function(da, "*", ax=ax, linewidth=1)
# test default color
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):
h = self.function(da, "*", ax=ax)

assert q.get_hatch_linewidth() == 1
assert mpl.rcParams["hatch.linewidth"] == 1
assert mpl.colors.to_rgba("0.1") == get_hatchcolor(h)

q = function(da, "*", ax=ax)
assert q.get_hatch_linewidth() == 0.25
assert mpl.rcParams["hatch.linewidth"] == 0.25
# different colors can be set
with subplots_context(1, 1, subplot_kw=self.subplot_kw) as (__, ax):

# changing away from the linewidth does NOT raise a warning
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
h = self.function(da, "*", ax=ax, color="#2ca25f")
assert mpl.colors.to_rgba("#2ca25f") == get_hatchcolor(h)

q = function(da, "*", ax=ax, linewidth=2)
assert q.get_hatch_linewidth() == 2
assert mpl.rcParams["hatch.linewidth"] == 2
h = self.function(da, "*", ax=ax, color="#e5f5f9")
assert mpl.colors.to_rgba("#e5f5f9") == get_hatchcolor(h)

with assert_no_warnings():
q = function(da, "*", ax=ax, linewidth=1)

assert q.get_hatch_linewidth() == 1
assert mpl.rcParams["hatch.linewidth"] == 1
class TestHatch(HatchBase):

function = staticmethod(mpu.hatch)
subplot_kw = {}

@pytest.mark.parametrize("function", HATCH_FUNCTIONS)
def test_hatch_color(function):

da = xr.DataArray(
np.ones([3, 3], dtype=bool),
dims=("lat", "lon"),
coords={"lat": [0, 1, 2], "lon": [1, 2, 3]},
)
class TestHatchMap(HatchBase):

function = staticmethod(mpu.hatch_map_global)
subplot_kw = {"projection": ccrs.PlateCarree()}

# test default color
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
h = function(da, "*", ax=ax)

assert mpl.colors.to_rgba("0.1") == get_hatchcolor(h)

# different colors can be set
with subplots_context(1, 1, subplot_kw=subplot_kw) as (__, ax):
class TestHatchMapGlobal(HatchBase):

h = function(da, "*", ax=ax, color="#2ca25f")
assert mpl.colors.to_rgba("#2ca25f") == get_hatchcolor(h)

h = function(da, "*", ax=ax, color="#e5f5f9")
assert mpl.colors.to_rgba("#e5f5f9") == get_hatchcolor(h)
function = staticmethod(mpu.hatch_map_global)
subplot_kw = {"projection": ccrs.PlateCarree()}


def test_hatch_bbox():
Expand Down