From 242238e44bf25aa2031872a54c349e96708bc5b1 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Tue, 23 Jan 2024 14:14:32 -0800 Subject: [PATCH 1/5] Handling the case where admin boundary has no residents. RE:#1503 --- src/natcap/invest/urban_nature_access.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/natcap/invest/urban_nature_access.py b/src/natcap/invest/urban_nature_access.py index c0fc762966..d551ff4562 100644 --- a/src/natcap/invest/urban_nature_access.py +++ b/src/natcap/invest/urban_nature_access.py @@ -347,9 +347,11 @@ "type": "number", "units": u.m**2/u.person, "about": ( - "The averge urban nature supply/demand " + "The average urban nature supply/demand " "balance available per person within this " - "administrative unit.") + "administrative unit. If no people reside " + "within this administrative unit, this value " + "will be NaN.") }, "Pund_adm": { "type": "number", @@ -2050,12 +2052,18 @@ def _get_zonal_stats(raster_path): stats_by_feature = {} for fid in urban_nature_budget_stats.keys(): stats = { - 'SUP_DEMadm_cap': ( - urban_nature_budget_stats[fid]['sum'] / - population_stats[fid]['sum']), 'Pund_adm': undersupplied_stats[fid]['sum'], 'Povr_adm': oversupplied_stats[fid]['sum'], } + + # Handle the case where an administrative unit might overlap no people + try: + stats['SUP_DEMadm_cap'] = ( + urban_nature_budget_stats[fid]['sum'] / + population_stats[fid]['sum']) + except ZeroDivisionError: + stats['SUP_DEMadm_cap'] = float('nan') + for pop_group_field in pop_group_fields: group = group_names[pop_group_field] group_proportion = pop_proportions_by_fid[fid][group] From 6c99386e78b5825a88c44dbed93e40f077dfd1a3 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Tue, 23 Jan 2024 14:15:23 -0800 Subject: [PATCH 2/5] Starting a test for case where there are no residents. RE:#1503 --- tests/test_urban_nature_access.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_urban_nature_access.py b/tests/test_urban_nature_access.py index 2cd43c1b29..5eaeacda53 100644 --- a/tests/test_urban_nature_access.py +++ b/tests/test_urban_nature_access.py @@ -839,6 +839,29 @@ def test_write_vector(self): layer = None vector = None + def test_write_vector_for_single_raster_modes(self): + """UNA: create a summary vector for single-raster summary stats.""" + + from natcap.invest import urban_nature_access + + args = _build_model_args(self.workspace_dir) + + # Overwrite all population pixels with 0 + try: + raster = gdal.Open(args['population_raster_path'], gdal.GA_Update) + band = raster.GetRasterBand(1) + array = band.ReadAsArray() + array.fill(0.0) + band.WriteArray(array) + finally: + raster = band = None + + args['search_radius_mode'] = urban_nature_access.RADIUS_OPT_UNIFORM + args['search_radius'] = 1000 + + urban_nature_access.execute(args) + + def test_urban_nature_proportion(self): """UNA: Run the model with urban nature proportion.""" from natcap.invest import urban_nature_access From 7b840f2589d41b98c84b06e0bc8a38732fafa40e Mon Sep 17 00:00:00 2001 From: James Douglass Date: Tue, 23 Jan 2024 14:36:58 -0800 Subject: [PATCH 3/5] Restructuring code to avoid ZeroDivision warning. RE:#1503 --- src/natcap/invest/urban_nature_access.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/natcap/invest/urban_nature_access.py b/src/natcap/invest/urban_nature_access.py index 8dc5c4d905..1be25a1add 100644 --- a/src/natcap/invest/urban_nature_access.py +++ b/src/natcap/invest/urban_nature_access.py @@ -2057,12 +2057,13 @@ def _get_zonal_stats(raster_path): } # Handle the case where an administrative unit might overlap no people - try: - stats['SUP_DEMadm_cap'] = ( + if population_stats[fid]['sum'] == 0: + per_capita_supply = float('nan') + else: + per_capita_supply = ( urban_nature_budget_stats[fid]['sum'] / population_stats[fid]['sum']) - except ZeroDivisionError: - stats['SUP_DEMadm_cap'] = float('nan') + stats['SUP_DEMadm_cap'] = per_capita_supply for pop_group_field in pop_group_fields: group = group_names[pop_group_field] From 1f919458bcb437e90a469c0518aac12a4c1ce950 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Tue, 23 Jan 2024 14:37:35 -0800 Subject: [PATCH 4/5] Testing. RE:#1503 --- src/natcap/invest/urban_nature_access.py | 5 +++-- tests/test_urban_nature_access.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/natcap/invest/urban_nature_access.py b/src/natcap/invest/urban_nature_access.py index 1be25a1add..f856da5dda 100644 --- a/src/natcap/invest/urban_nature_access.py +++ b/src/natcap/invest/urban_nature_access.py @@ -350,8 +350,9 @@ "The average urban nature supply/demand " "balance available per person within this " "administrative unit. If no people reside " - "within this administrative unit, this value " - "will be NaN.") + "within this administrative unit, this field " + "will have no value (NaN, NULL or None, " + "depending on your GIS software).") }, "Pund_adm": { "type": "number", diff --git a/tests/test_urban_nature_access.py b/tests/test_urban_nature_access.py index 5906ca1319..4de4c58b9c 100644 --- a/tests/test_urban_nature_access.py +++ b/tests/test_urban_nature_access.py @@ -937,6 +937,25 @@ def test_write_vector_for_single_raster_modes(self): urban_nature_access.execute(args) + summary_vector = gdal.OpenEx( + os.path.join(args['workspace_dir'], 'output', + 'admin_boundaries_suffix.gpkg')) + summary_layer = summary_vector.GetLayer() + self.assertEqual(summary_layer.GetFeatureCount(), 1) + summary_feature = summary_layer.GetFeature(1) + + expected_field_values = { + 'adm_unit_id': 0, + 'Pund_adm': 0, + 'Povr_adm': 0, + 'SUP_DEMadm_cap': None, # OGR converts NaN to None. + } + self.assertEqual( + set(defn.GetName() for defn in summary_layer.schema), + set(expected_field_values.keys())) + for fieldname, expected_value in expected_field_values.items(): + self.assertAlmostEqual( + expected_value, summary_feature.GetField(fieldname)) def test_urban_nature_proportion(self): """UNA: Run the model with urban nature proportion.""" From 9f4afe85360cce2fe36ee74ba134e9f80b0f6e98 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Tue, 23 Jan 2024 14:40:18 -0800 Subject: [PATCH 5/5] Noting change in HISTORY. RE:#1503 --- HISTORY.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index c5c79f61d8..7a0341fc57 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -42,6 +42,9 @@ Unreleased Changes * Fixed a ``NameError`` that occurred when running the model using search radii defined per population group with an exponential search kernel. https://github.com/natcap/invest/issues/1502 + * Fixed an issue where Urban Nature Access would crash if an administrative + boundary geometry did not overlap any people in the population raster. + https://github.com/natcap/invest/issues/1503 3.14.1 (2023-12-18) -------------------