Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UNA: allow the LULC raster to not have a nodata value defined #1631

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
4 changes: 4 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
Unreleased Changes
------------------
* Urban Nature Access
* The model now works as expected when the user provides an LULC raster
that does not have a nodata value defined.
https://github.com/natcap/invest/issues/1293
* Workbench
* Several small updates to the model input form UI to improve usability
and visual consistency (https://github.com/natcap/invest/issues/912)
Expand Down
45 changes: 39 additions & 6 deletions src/natcap/invest/urban_nature_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,13 +755,12 @@ def execute(args):
squared_lulc_pixel_size = _square_off_pixels(args['lulc_raster_path'])

lulc_alignment_task = graph.add_task(
pygeoprocessing.warp_raster,
_warp_lulc,
kwargs={
'base_raster_path': args['lulc_raster_path'],
'target_pixel_size': squared_lulc_pixel_size,
'target_bb': target_bounding_box,
'target_raster_path': file_registry['aligned_lulc'],
'resample_method': 'near',
"source_lulc_path": args['lulc_raster_path'],
"target_lulc_path": file_registry['aligned_lulc'],
"target_pixel_size": squared_lulc_pixel_size,
"target_bounding_box": target_bounding_box,
},
target_path_list=[file_registry['aligned_lulc']],
task_name='Resample LULC to have square pixels'
Expand Down Expand Up @@ -2528,6 +2527,40 @@ def _create_mask(*raster_arrays):
_create_mask, target_mask_path, gdal.GDT_Byte, nodata_target=255)


def _warp_lulc(source_lulc_path, target_lulc_path, target_pixel_size,
target_bounding_box):
"""Warp a LULC raster and set a nodata if needed.

Args:
source_lulc_path (str): The path to a source LULC raster.
target_lulc_path (str): The path to the new LULC raster.
target_pixel_size (tuple): A 2-tuple of the target pixel size.
target_bounding_box (tuple): A 4-tuple of the target bounding box.

Returns:
``None``.
"""
source_raster_info = pygeoprocessing.get_raster_info(source_lulc_path)
target_nodata = source_raster_info['nodata'][0]

pygeoprocessing.warp_raster(
source_lulc_path, target_pixel_size, target_lulc_path,
'near', target_bb=target_bounding_box,
target_projection_wkt=source_raster_info['projection_wkt'])

# if there is no defined nodata, set a default value
if target_nodata is None:
# Guarantee that our nodata cannot be represented by the datatype -
# select a nodata value that's out of range.
target_nodata = pygeoprocessing.choose_nodata(
source_raster_info['numpy_type']) + 1
raster = gdal.OpenEx(target_lulc_path, gdal.GA_Update)
band = raster.GetRasterBand(1)
band.SetNoDataValue(target_nodata)
band = None
raster = None


def _mask_raster(source_raster_path, mask_raster_path, target_raster_path):
"""Convert pixels to nodata given an existing mask raster.

Expand Down
15 changes: 15 additions & 0 deletions tests/test_urban_nature_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,21 @@ def test_core_model(self):
self.assertAlmostEqual(numpy.min(valid_pixels), 1171.7352294921875)
self.assertAlmostEqual(numpy.max(valid_pixels), 11898.0712890625)

def test_no_lulc_nodata(self):
"""UNA: verify behavior when the LULC has no nodata value."""
from natcap.invest import urban_nature_access

args = _build_model_args(self.workspace_dir)
args['search_radius_mode'] = urban_nature_access.RADIUS_OPT_UNIFORM
args['search_radius'] = 100

raster = gdal.OpenEx(args['lulc_raster_path'], gdal.OF_RASTER)
band = raster.GetRasterBand(1)
band.DeleteNoDataValue()
band = None
raster = None
urban_nature_access.execute(args)

def test_split_urban_nature(self):
from natcap.invest import urban_nature_access

Expand Down
Loading