From 0ceaf79aad18ab3e073d534979b5252063134e69 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Sun, 27 Aug 2023 11:28:26 -0700 Subject: [PATCH] Fixing the list of available warp names. gdal.WarpOptions in GDAL < 3.4 did not handle all supported gdal.GRA_* resampling options, instead using the incomplete set of options under gdal.GRIORA_*. This was fixed in GDAL 3.4. The change introduced in this commit handles both approaches to interpreting the resample algorithm names available with this rough algorithm: 1. If gdal.WarpOptions() handles the name, use that. 2. Otherwise (if the name is unknown because of the gdal.GRIORA_* issue or if a shorthand is used instead, like '-rb' instead of '-r bilinear'), interpret the name from the GRA_* attribute name. Testing this solution on my computer works on GDAL < 3.4 (3.3 tested) and also on GDAL >= 3.4 (3.7 tested). RE:#326 --- src/pygeoprocessing/geoprocessing.py | 46 +++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/pygeoprocessing/geoprocessing.py b/src/pygeoprocessing/geoprocessing.py index d5b0dfb8..869ead2c 100644 --- a/src/pygeoprocessing/geoprocessing.py +++ b/src/pygeoprocessing/geoprocessing.py @@ -7,6 +7,7 @@ import os import pprint import queue +import re import shutil import sys import tempfile @@ -81,17 +82,46 @@ def __init__(self, missing_values, raster_path, value_map): gdal.GDT_CFloat64: numpy.complex64, } -# GDAL's python API recognizes certain strings but the only way to retrieve -# those strings is to do this conversion of gdalconst.GRA_* types to the -# human-readable labels via gdal.WarpOptions like so. _GDAL_WARP_ALGORITHMS = [] for _warp_algo in (_attrname for _attrname in dir(gdalconst) if _attrname.startswith('GRA_')): - # Appends ['-r', 'near'] to _GDAL_WARP_ALGORITHMS if _warp_algo is - # gdalconst.GRA_NearestNeighbor. See gdal.WarpOptions for the dict - # defining this mapping. - gdal.WarpOptions(options=_GDAL_WARP_ALGORITHMS, - resampleAlg=getattr(gdalconst, _warp_algo)) + # In GDAL < 3.4, the options used were actually gdal.GRIORA_* + # instead of gdal.GRA_*, and gdal.WarpOptions would: + # * return an integer for any gdal.GRIORA options it didn't recognize + # * Return an abbreviated string (e.g. -rb instead of 'bilinear') for + # several warp algorithms + # This behavior was changed in GDAL 3.4, where the correct name is + # returned in all cases. + # + # In GDAL < 3.4, an error would be logged if GDAL couldn't recognize the + # option. Pushing a null error handler avoids this and cleans up the + # logging. + gdal.PushErrorHandler(lambda *args: None) + _options = [] + # GDAL's python bindings only offer this one way of translating gdal.GRA_* + # options to their string names (but compatibility is limited in different + # GDAL versions, see notes above) + warp_opts = gdal.WarpOptions( + options=_options, # Add rendered flags to this list. + resampleAlg=getattr(gdalconst, _warp_algo), # use GRA_* name. + overviewLevel=None) # Don't add overview parameters. + gdal.PopErrorHandler() + + for _item in _options: + is_int = False + try: + int(_item) + is_int = True + except ValueError: + pass + + if _item == '-r': + continue + elif (_item.startswith('-r') and len(_item) > 2) or is_int: + # (GDAL < 3.4) Translate shorthand params to name. + # (GDAL < 3.4) Translate unrecognized (int) codes to name. + _item = re.sub('^gra_', '', _warp_algo.lower()) + _GDAL_WARP_ALGORITHMS = set(_GDAL_WARP_ALGORITHMS) _GDAL_WARP_ALGORITHMS.discard('-r') _GDAL_WARP_ALGOS_FOR_HUMAN_EYES = "|".join(_GDAL_WARP_ALGORITHMS)