From 3ccd820f9f3f43014a672e223a9b0b29fd947fbc Mon Sep 17 00:00:00 2001 From: Jakub Krasuski Date: Sun, 4 Oct 2026 20:46:30 +0200 Subject: [PATCH 1/2] Fix ExclusionContainer.add_raster ignoring codes=0 shape_availability checked the truthiness of codes, so the integer 0 was handled like a missing codes argument: all non-zero cells were excluded instead of the cells with value 0. A numpy array raised a ValueError for the same reason. Check for None instead. --- RELEASE_NOTES.rst | 7 +++++++ atlite/gis.py | 4 ++-- test/test_gis.py | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index acbb91bd..25c6db88 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -17,6 +17,13 @@ Upcoming Release To use the features already you have to install the ``master`` branch, e.g. ``pip install git+https://github.com/pypsa/atlite``. +**Bug fixes** + +* Fix ``ExclusionContainer.add_raster`` ignoring ``codes=0``. The code ``0`` was + treated like a missing ``codes`` argument, so all non-zero cells were excluded + instead of the cells with value ``0``. Numpy arrays are now accepted as + ``codes``, and an empty list excludes no cells. + `v0.7.0 `__ (3rd October 2026) ======================================================================================= diff --git a/atlite/gis.py b/atlite/gis.py index 21efc60e..7e2286a0 100644 --- a/atlite/gis.py +++ b/atlite/gis.py @@ -459,7 +459,7 @@ def shape_availability( masked, transform = projected_mask( d["raster"], geometry, transform, shape, excluder.crs, **kwargs ) - if d["codes"]: + if d["codes"] is not None: if callable(d["codes"]): masked_ = d["codes"](masked).astype(bool) else: @@ -598,7 +598,7 @@ def add_raster( which takes the mask (np.array) as argument and performs a elementwise condition (must not change the shape). The function may not be an anonymous (lambda) function. - The default is 1. + The default is None, which excludes all non-zero cells. buffer : int, optional Buffer around the excluded areas in units of ExclusionContainer.crs. Use this to create a buffer around the excluded/included area. diff --git a/test/test_gis.py b/test/test_gis.py index 89210b33..e19a0d50 100755 --- a/test/test_gis.py +++ b/test/test_gis.py @@ -636,6 +636,23 @@ def test_shape_availability_exclude_raster_codes(ref, raster_codes): assert ratio == masked.sum() / masked.size +@pytest.mark.parametrize("codes", [0, np.array([0])]) +def test_shape_availability_exclude_raster_code_zero(ref, raster, codes): + """Code 0 is excluded like any other code, not treated as a missing code.""" + shapes = gpd.GeoSeries([box(X0, Y0, X1, Y1)], crs=ref.crs) + res = 0.01 + + excluder = ExclusionContainer(ref.crs, res=res) + excluder.add_raster(raster, codes=[0]) + expected, transform = shape_availability(shapes, excluder) + + excluder = ExclusionContainer(ref.crs, res=res) + excluder.add_raster(raster, codes=codes) + masked, transform = shape_availability(shapes, excluder) + assert (masked == expected).all() + assert round(masked.sum() / masked.size, 2) == raster_clip + + def test_plot_shape_availability(ref, raster): """Test plotting of shape availability.""" shapes = gpd.GeoSeries([box(X0, Y0, X1, Y1)], crs=ref.crs) From 7e642a3b3439992905cd8fc5cd8df9edd0aafbd8 Mon Sep 17 00:00:00 2001 From: Jakub Krasuski Date: Sun, 4 Oct 2026 22:49:41 +0200 Subject: [PATCH 2/2] Add pull request link to release note --- RELEASE_NOTES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 25c6db88..6b991c7b 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -22,7 +22,8 @@ Upcoming Release * Fix ``ExclusionContainer.add_raster`` ignoring ``codes=0``. The code ``0`` was treated like a missing ``codes`` argument, so all non-zero cells were excluded instead of the cells with value ``0``. Numpy arrays are now accepted as - ``codes``, and an empty list excludes no cells. + ``codes``, and an empty list excludes no cells + (https://github.com/PyPSA/atlite/pull/525). `v0.7.0 `__ (3rd October 2026) =======================================================================================