From 5a2bea7467dfc6ffa07362ab54e75673d07ef5af Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 6 Mar 2026 14:52:56 -0800 Subject: [PATCH 1/2] Eliminating side effect with an array copy. Tests included. RE:#471 --- src/pygeoprocessing/geoprocessing.py | 6 +++++- tests/test_geoprocessing.py | 28 +++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/pygeoprocessing/geoprocessing.py b/src/pygeoprocessing/geoprocessing.py index 5af4bef7..97cafa77 100644 --- a/src/pygeoprocessing/geoprocessing.py +++ b/src/pygeoprocessing/geoprocessing.py @@ -37,7 +37,8 @@ from .geoprocessing_core import DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS from .geoprocessing_core import DEFAULT_OSR_AXIS_MAPPING_STRATEGY from .geoprocessing_core import INT8_CREATION_OPTIONS -from .utils import GDALUseExceptions, gdal_use_exceptions +from .utils import gdal_use_exceptions +from .utils import GDALUseExceptions # This is used to efficiently pass data to the raster stats worker if available if sys.version_info >= (3, 8): @@ -1083,6 +1084,9 @@ def align_and_resize_raster_stack( [target_bounding_box, mask_vector_bb], 'intersection') if raster_align_index is not None and raster_align_index >= 0: + # ensure we are working with a copy of the bounding box so that the + # original is unmodified. + target_bounding_box = target_bounding_box[:] # bounding box needs alignment align_bounding_box = ( raster_info_list[raster_align_index]['bounding_box']) diff --git a/tests/test_geoprocessing.py b/tests/test_geoprocessing.py index 75a4522b..6fcb3063 100644 --- a/tests/test_geoprocessing.py +++ b/tests/test_geoprocessing.py @@ -32,10 +32,10 @@ from pygeoprocessing.geoprocessing_core import DEFAULT_CREATION_OPTIONS from pygeoprocessing.geoprocessing_core import \ DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS -from pygeoprocessing.utils import gdal_use_exceptions from pygeoprocessing.geoprocessing_core import INT8_CREATION_OPTIONS from pygeoprocessing.geoprocessing_core import \ INT8_GTIFF_CREATION_TUPLE_OPTIONS +from pygeoprocessing.utils import gdal_use_exceptions _DEFAULT_ORIGIN = (444720, 3751320) _DEFAULT_PIXEL_SIZE = (30, -30) @@ -2506,17 +2506,39 @@ def test_align_and_resize_raster_stack_bb(self): # goes first in the following bounding box construction base_a_raster_info = pygeoprocessing.get_raster_info(base_a_path) + # Make a new bounding box that's in between the two and not perfectly + # aligned to the pixels in base_a_raster. + xmin, ymin, xmax, ymax = base_a_raster_info['bounding_box'] + target_bbox = [ + xmin+23, + ymin+23, + xmax-46, + ymax-46, + ] + + # REGRESSION TEST: make sure this input variable is unmodified after + # execution + # https://github.com/natcap/pygeoprocessing/issues/471 + target_bbox_copy = target_bbox[:] + pygeoprocessing.align_and_resize_raster_stack( base_raster_path_list, target_raster_path_list, resample_method_list, - base_a_raster_info['pixel_size'], 'intersection', + base_a_raster_info['pixel_size'], + target_bbox, base_vector_path_list=None, raster_align_index=0) + # REGRESSION TEST: make sure this input variable is unmodified after + # execution. + # https://github.com/natcap/pygeoprocessing/issues/471 + self.assertEqual(target_bbox, target_bbox_copy) + # we expect this to be twice as big since second base raster has a # pixel size twice that of the first. target_array = pygeoprocessing.raster_to_numpy_array( target_raster_path_list[0]) - numpy.testing.assert_array_equal(pixel_a_matrix, target_array) + numpy.testing.assert_array_equal( + numpy.ones((4, 4), pixel_a_matrix.dtype), target_array) def test_raster_calculator(self): """PGP.geoprocessing: raster_calculator identity test.""" From cfc2fa53a162dfc787716ca2ba2abd2d257f0848 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 6 Mar 2026 14:55:38 -0800 Subject: [PATCH 2/2] Noting change in history. RE:#471 --- HISTORY.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 2fc61c9f..d9f4fba2 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -16,6 +16,10 @@ Unreleased Changes * Updating pyproject.toml to use the standard ``license-files`` key and replacing the license-related Trove classifier with the approved SPDX string. https://github.com/natcap/pygeoprocessing/issues/466 +* Fixing a side effect in ``pygeoprocessing.align_and_resize_raster_stack`` + where aligning a raster stack using the bounding box mode would result in the + input bounding box being modified in-place. + https://github.com/natcap/pygeoprocessing/issues/471 2.4.10 (2026-01-13) -------------------