Skip to content

Commit

Permalink
Merge pull request #420 from claire-simpson/check-na-key
Browse files Browse the repository at this point in the history
Check for non-numeric keys in value_map in reclassify raster function #419
  • Loading branch information
davemfish authored Dec 19, 2024
2 parents 8982604 + 47cafc4 commit a9fe8df
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 8 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Release History
* Dropped support for Python 3.8. Added support for Python 3.13 and GDAL 3.9.
https://github.com/natcap/pygeoprocessing/issues/415
* Added validation to ``reclassify_raster`` to raise a ``TypeError`` with a
descriptive message if ``value_map`` contains non-numeric keys.

2.4.6 (2024-10-15)
------------------
Expand Down
12 changes: 12 additions & 0 deletions src/pygeoprocessing/geoprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2338,6 +2338,12 @@ def reclassify_raster(
if ``values_required`` is ``True``
and a pixel value from ``base_raster_path_band`` is not a key in
``value_map``.
ValueError
- if ``value_map`` is empty
- if ``base_raster_path_band`` is formatted incorrectly
- if nodata value not set
TypeError
if there are non-numeric keys in ``value_map``
"""
if len(value_map) == 0:
Expand All @@ -2346,6 +2352,12 @@ def reclassify_raster(
raise ValueError(
"Expected a (path, band_id) tuple, instead got '%s'" %
base_raster_path_band)
# raise error if there are any non-numeric keys in value_map
nonnumeric = [key for key in value_map
if not isinstance(key, (int, float, numpy.number))]
if nonnumeric:
raise TypeError(f"Non-numeric key(s) in value map: {nonnumeric}")

raster_info = get_raster_info(base_raster_path_band[0])
nodata = raster_info['nodata'][base_raster_path_band[1]-1]
# If nodata was included in the value_map pop it from our lists
Expand Down
28 changes: 20 additions & 8 deletions tests/test_geoprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ def test_reclassify_raster_missing_pixel_value(self):
value_map = {
test_value: 100,
}
target_nodata = -1
with self.assertRaises(
pygeoprocessing.ReclassificationMissingValuesError) as cm:
pygeoprocessing.reclassify_raster(
Expand All @@ -187,6 +186,26 @@ def test_reclassify_raster_missing_pixel_value(self):
actual_message = str(cm.exception)
self.assertIn(expected_message, actual_message)

def test_reclassify_raster_nonnumeric_key(self):
"""PGP.geoprocessing: test reclassify raster with non-numeric key
in value_map."""
n_pixels = 9
pixel_matrix = numpy.ones((n_pixels, n_pixels), numpy.float32)
target_nodata = -1
raster_path = os.path.join(self.workspace_dir, 'raster.tif')
target_path = os.path.join(self.workspace_dir, 'target.tif')
_array_to_raster(
pixel_matrix, target_nodata, raster_path)

value_map = {1: 2, None: 3, "s": 4, numpy.nan: 5, numpy.float32(99): 6}
with self.assertRaises(TypeError) as e:
pygeoprocessing.reclassify_raster(
(raster_path, 1), value_map, target_path, gdal.GDT_Float32,
target_nodata, values_required=False)
expected_message = "Non-numeric key(s) in value map: [None, 's']"
actual_message = str(e.exception)
self.assertIn(expected_message, actual_message)

def test_reclassify_raster(self):
"""PGP.geoprocessing: test reclassify raster."""
n_pixels = 9
Expand All @@ -202,7 +221,6 @@ def test_reclassify_raster(self):
value_map = {
test_value: 100,
}
target_nodata = -1
pygeoprocessing.reclassify_raster(
(raster_path, 1), value_map, target_path, gdal.GDT_Float32,
target_nodata, values_required=True)
Expand All @@ -225,7 +243,6 @@ def test_reclassify_raster_no_raster_path_band(self):
value_map = {
test_value: 100,
}
target_nodata = -1
# we expect a value error because we didn't pass a (path, band)
# for the first argument
with self.assertRaises(ValueError):
Expand All @@ -247,7 +264,6 @@ def test_reclassify_raster_empty_value_map(self):

empty_value_map = {
}
target_nodata = -1
with self.assertRaises(ValueError):
pygeoprocessing.reclassify_raster(
(raster_path, 1), empty_value_map, target_path,
Expand Down Expand Up @@ -2098,7 +2114,6 @@ def test_align_and_resize_raster_stack_int_with_vectors(self):
pixel_a_matrix, target_nodata, base_a_path)

pixel_b_matrix = numpy.ones((15, 15), numpy.int16)
target_nodata = -1
base_b_path = os.path.join(self.workspace_dir, 'base_b.tif')
_array_to_raster(
pixel_b_matrix, target_nodata, base_b_path)
Expand Down Expand Up @@ -2205,7 +2220,6 @@ def test_align_and_resize_raster_stack_no_overlap(self):
pixel_a_matrix, target_nodata, base_a_path, origin=[-10*30, 10*30])

pixel_b_matrix = numpy.ones((15, 15), numpy.int16)
target_nodata = -1
base_b_path = os.path.join(self.workspace_dir, 'base_b.tif')
_array_to_raster(pixel_b_matrix, target_nodata, base_b_path)

Expand Down Expand Up @@ -2248,7 +2262,6 @@ def test_align_and_resize_raster_stack_union(self):
pixel_a_matrix, target_nodata, base_a_path, pixel_size=(30, -30))

pixel_b_matrix = numpy.ones((10, 10), numpy.int16)
target_nodata = -1
base_b_path = os.path.join(self.workspace_dir, 'base_b.tif')
_array_to_raster(
pixel_b_matrix, target_nodata, base_b_path, pixel_size=(60, -60))
Expand Down Expand Up @@ -2288,7 +2301,6 @@ def test_align_and_resize_raster_stack_bb(self):
pixel_a_matrix, target_nodata, base_a_path, pixel_size=(30, -30))

pixel_b_matrix = numpy.ones((10, 10), numpy.int16)
target_nodata = -1
base_b_path = os.path.join(self.workspace_dir, 'base_b.tif')
_array_to_raster(
pixel_b_matrix, target_nodata, base_b_path, pixel_size=(30, -30))
Expand Down

0 comments on commit a9fe8df

Please sign in to comment.