-
Notifications
You must be signed in to change notification settings - Fork 67
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
Changes from 4 commits
3294e6a
eca7a62
b6339e6
e6eb1a0
94f7f00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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' | ||
|
@@ -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] | ||
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 | ||
|
||
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 | ||
raster = gdal.OpenEx(target_lulc_path, gdal.GA_Update) | ||
band = raster.GetRasterBand(1) | ||
band.SetNoDataValue(target_nodata) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Forgive me if I'm just misreading something, but could this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You know, it shouldn't be an issue because |
||
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. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about the need to add 1 here. I mean, I understand from the comment why you've done this, but I wonder if you think
choose_nodata
should be updated to return the max + 1 instead of just the max? Would that make sense for other use cases, or could it cause problems?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question! Since we can assume that every pixel in the dataset is valid (because
target_nodata is None
), the safest possible nodata value is one that isn't even in the range of values possible with the given datatype. For afloat32
raster, it's pretty unlikely that we would have a dataset that would have every discrete value that is allowed by the float32 spec, but it could still happen. Since the nodata value is stored as a piece of metadata separate from the pixel values themselves, we can avoid this possibility completely by using a nodata value that is completely outside of the range of possibilities of the datatype of the pixel values themselves.If we were to change the datatype of
choose_nodata
, you are right that that would unfortunately break things. Inraster_map
, the nodata values of the incoming raster are transformed to the nodata values of the target raster, where the target nodata has been selected bychoose_nodata
. Ifchoose_nodata
were to return a value of the (dtype's max value)+1, then we'd get an overflow in the pixel values, or we bump up the output raster's datatype to accommodate the extra needed precision, neither of which we want!So this solution as implemented gets around the situation by only handling this one case, where the lulc doesn't have a nodata value defined, and we just set one (to avoid breaking other things later on in the model) that can never happen in this datatype.