fire2a.raster.gdal_calc_norm



usage: gdal_calc_norm.py [-h] [-i INFILE] [-o OUTFILE]
                         [-m {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}]
                         [-min MINIMUM] [-max MAXIMUM] [-n [NODATAVALUE]] [-f FORMAT]
                         [-t {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}]
                         [-p min_x max_y max_x min_y] [-r]
                         [params ...]

Raster normalization utility, wrapping on osgeo_utils.gdal_calc with a set of predefined normalization methods. Run `gdal_calc.py
--help` for more information.

positional arguments:
  params                Float numbers according to the normalizing method. None: for minmax, maxmin; One for stepup, stepdown; Two for
                        bipiecewiselinear, bipiecewiselinear_percent, see also method (default: None)

options:
  -h, --help            show this help message and exit
  -i INFILE, --infile INFILE
                        Input file (default: infile.tif)
  -o OUTFILE, --outfile OUTFILE
                        Output file (default: outfile.tif)
  -m {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}, --method {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}
                        Method to normalize the input, see also params (default: minmax)
  -min MINIMUM, --minimum MINIMUM
                        Minimun value for minmax/maxmin (else the value is calculated from the WHOLE input raster). (default: None)
  -max MAXIMUM, --maximum MAXIMUM
                        Maximum value for minmax/maxmin (else the value is calculated from the WHOLE input raster). (default: None)
  -n [NODATAVALUE], --NoDataValue [NODATAVALUE]
                        output nodata value (send null for default datatype specific, see `from osgeo_utils.gdal_calc import
                        DefaultNDVLookup`) (default: -9999)
  -f FORMAT, --format FORMAT
                        Output format (send null to infer from file, see `from osgeo_utils.auxiliary.util import GetOutputDriverFor`)
                        (default: GTiff)
  -t {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}, --type {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}
                        Output datatype (default: Float32)
  -p min_x max_y max_x min_y, --projwin min_x max_y max_x min_y
                        An optional list of 4 coordinates defining the projection window, if not provided the whole raster is
                        calculated (default: None)
  -r, --return_dataset  Return dataset (for scripting -additional keyword arguments are passed to gdal_calc.Calc) instead of return
                        code (default: False)

documentation at https://fire2a.github.io/fire2a-lib/fire2a/raster/gdal_calc_norm.html


Sample script usage:
    from fire2a.raster.gdal_calc_norm import main
    ds = main(["-r", ... other keyword arguments are passed to gdal_calc.Calc

function osgeo_utils.gdal_calc.Calc(
    calc: Union[str, Sequence[str]],
    outfile: Union[str, os.PathLike, NoneType] = None,
    NoDataValue: Optional[numbers.Number] = None,
    type: Union[int, str, NoneType] = None,
    format: Optional[str] = None,
    creation_options: Optional[Sequence[str]] = None,
    allBands: str = '',
    overwrite: bool = False,
    hideNoData: bool = False,
    projectionCheck: bool = False,
    color_table: Union[osgeo.gdal.ColorTable,
    ForwardRef('ColorPalette'), str, os.PathLike, Sequence[str], NoneType] = None,
    extent: Optional[osgeo_utils.auxiliary.extent_util.Extent] = None,
    projwin: Union[Tuple, osgeo_utils.auxiliary.rectangle.GeoRectangle, NoneType] = None,
    user_namespace: Optional[Dict] = None,
    debug: bool = False,
    quiet: bool = False, **input_files)

/usr/bin/gdal_calc.py
/usr/lib/python3/dist-packages/osgeo_utils/gdal_calc.py
  1#!python
  2# fmt: off
  3"""
  4<pre><code>
  5<!-- BEGIN_ARGPARSE_DOCSTRING -->
  6usage: gdal_calc_norm.py [-h] [-i INFILE] [-o OUTFILE]
  7                         [-m {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}]
  8                         [-min MINIMUM] [-max MAXIMUM] [-n [NODATAVALUE]] [-f FORMAT]
  9                         [-t {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}]
 10                         [-p min_x max_y max_x min_y] [-r]
 11                         [params ...]
 12
 13Raster normalization utility, wrapping on osgeo_utils.gdal_calc with a set of predefined normalization methods. Run `gdal_calc.py
 14--help` for more information.
 15
 16positional arguments:
 17  params                Float numbers according to the normalizing method. None: for minmax, maxmin; One for stepup, stepdown; Two for
 18                        bipiecewiselinear, bipiecewiselinear_percent, see also method (default: None)
 19
 20options:
 21  -h, --help            show this help message and exit
 22  -i INFILE, --infile INFILE
 23                        Input file (default: infile.tif)
 24  -o OUTFILE, --outfile OUTFILE
 25                        Output file (default: outfile.tif)
 26  -m {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}, --method {minmax,maxmin,stepup,stepdown,bipiecewiselinear,bipiecewiselinear_percent,stepdown_percent,stepup_percent}
 27                        Method to normalize the input, see also params (default: minmax)
 28  -min MINIMUM, --minimum MINIMUM
 29                        Minimun value for minmax/maxmin (else the value is calculated from the WHOLE input raster). (default: None)
 30  -max MAXIMUM, --maximum MAXIMUM
 31                        Maximum value for minmax/maxmin (else the value is calculated from the WHOLE input raster). (default: None)
 32  -n [NODATAVALUE], --NoDataValue [NODATAVALUE]
 33                        output nodata value (send null for default datatype specific, see `from osgeo_utils.gdal_calc import
 34                        DefaultNDVLookup`) (default: -9999)
 35  -f FORMAT, --format FORMAT
 36                        Output format (send null to infer from file, see `from osgeo_utils.auxiliary.util import GetOutputDriverFor`)
 37                        (default: GTiff)
 38  -t {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}, --type {Byte,UInt16,Int16,UInt32,Int32,UInt64,Int64,Float32,Float64,CInt16,CInt32,CFloat32,CFloat64}
 39                        Output datatype (default: Float32)
 40  -p min_x max_y max_x min_y, --projwin min_x max_y max_x min_y
 41                        An optional list of 4 coordinates defining the projection window, if not provided the whole raster is
 42                        calculated (default: None)
 43  -r, --return_dataset  Return dataset (for scripting -additional keyword arguments are passed to gdal_calc.Calc) instead of return
 44                        code (default: False)
 45
 46documentation at https://fire2a.github.io/fire2a-lib/fire2a/raster/gdal_calc_norm.html
 47<!-- END_ARGPARSE_DOCSTRING -->
 48
 49Sample script usage:
 50    from fire2a.raster.gdal_calc_norm import main
 51    ds = main(["-r", ... other keyword arguments are passed to gdal_calc.Calc
 52
 53function osgeo_utils.gdal_calc.Calc(
 54    calc: Union[str, Sequence[str]],
 55    outfile: Union[str, os.PathLike, NoneType] = None,
 56    NoDataValue: Optional[numbers.Number] = None,
 57    type: Union[int, str, NoneType] = None,
 58    format: Optional[str] = None,
 59    creation_options: Optional[Sequence[str]] = None,
 60    allBands: str = '',
 61    overwrite: bool = False,
 62    hideNoData: bool = False,
 63    projectionCheck: bool = False,
 64    color_table: Union[osgeo.gdal.ColorTable,
 65    ForwardRef('ColorPalette'), str, os.PathLike, Sequence[str], NoneType] = None,
 66    extent: Optional[osgeo_utils.auxiliary.extent_util.Extent] = None,
 67    projwin: Union[Tuple, osgeo_utils.auxiliary.rectangle.GeoRectangle, NoneType] = None,
 68    user_namespace: Optional[Dict] = None,
 69    debug: bool = False,
 70    quiet: bool = False, **input_files)
 71
 72/usr/bin/gdal_calc.py
 73/usr/lib/python3/dist-packages/osgeo_utils/gdal_calc.py
 74</code></pre>
 75"""
 76import sys
 77
 78if sys.version_info < (3, 10):
 79    raise ImportError("This module requires Python 3.10 or higher")
 80
 81from pathlib import Path
 82from tempfile import NamedTemporaryFile
 83
 84from osgeo.gdal import Dataset, GA_ReadOnly, Open
 85
 86try:
 87    from osgeo_utils.auxiliary.util import GetOutputDriverFor
 88    from osgeo_utils.gdal_calc import Calc, GDALDataTypeNames
 89except ImportError:
 90    from osgeo.utils.gdal_calc import Calc, GetOutputDriverFor
 91
 92    GDALDataTypeNames = (
 93        "Byte",
 94        "UInt16",
 95        "Int16",
 96        "UInt32",
 97        "Int32",
 98        "UInt64",
 99        "Int64",
100        "Float32",
101        "Float64",
102        "CInt16",
103        "CInt32",
104        "CFloat32",
105        "CFloat64",
106    )
107
108
109def calc(
110    func,
111    outfile="outfile.tif",
112    infile="infile.tif",
113    band=1,
114    NoDataValue=None,
115    overwrite=True,
116    type="Float32",
117    format="GTiff",
118    projwin=None,
119    minimum=None,
120    maximum=None,
121    threshold=None,
122    a=None,
123    b=None,
124    r=None,
125    **kwargs,
126) -> Dataset:
127    """This is the wrapper function for the gdal_calc.Calc utility.
128
129    Although the normalization functions are symbolic strings defined in the main, passed as the `func` argument. Make sure to check them before using this function directly.
130
131    All extra keyword arguments are passed to the gdal_calc.Calc function."""
132    if isinstance(infile, Path):
133        infile = str(infile)
134    if isinstance(outfile, Path):
135        outfile = str(outfile)
136    if "method" in kwargs:
137        if kwargs["method"] in ["minmax", "maxmin", "bipiecewiselinear_percent", "stepup_percent", "stepdown_percent"]:
138            if minimum is None and maximum is None:
139                minimum, maximum = get_file_minmax(infile)
140                print(f"{minimum=}, {maximum=}")
141            if minimum is None:
142                minimum, _ = get_file_minmax(infile)
143                print(f"{minimum=}")
144            if maximum is None:
145                _, maximum = get_file_minmax(infile)
146                print(f"{maximum=}")
147        # drop before passing to Calc
148        del kwargs["method"]
149    # if not projwin:
150    #     info = locals().get("info", read_raster(infile, data=False)[1])
151    #     projwin = get_projwin(info["Transform"], info["RasterXSize"], info["RasterYSize"])
152    #     print(f"{projwin=}")
153
154    # get a the parameter values of the calc function
155    code_obj = func.__code__
156    local_var_names = code_obj.co_varnames[: code_obj.co_nlocals]
157    func_vars = []
158    for var in local_var_names:
159        func_vars += [locals().get(var)]
160    if "r" in local_var_names:
161        func_vars[-1] = (maximum - minimum) / 100
162    print(f"{dict(zip(local_var_names,func_vars))=}")
163    print(f"{local_var_names=}")
164    print(f"{func_vars=}")
165
166    dataset = Calc(
167        calc=func(*func_vars),
168        outfile=outfile,
169        A=infile,
170        A_band=band,
171        NoDataValue=NoDataValue,
172        overwrite=overwrite,
173        type=type,
174        format=format,
175        projwin=projwin,
176        **kwargs,
177    )
178    dataset.FlushCache()
179
180    return dataset
181
182
183def arg_parser(argv=None):
184    """Parse arguments list"""
185    from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
186
187    parser = ArgumentParser(
188        description="Raster normalization utility, wrapping on osgeo_utils.gdal_calc with a set of predefined normalization methods. Run `gdal_calc.py --help` for more information.",
189        formatter_class=ArgumentDefaultsHelpFormatter,
190        epilog="documentation at https://fire2a.github.io/fire2a-lib/fire2a/raster/gdal_calc_norm.html",
191    )
192    parser.add_argument(
193        "params",
194        nargs="*",
195        type=float,
196        help="Float numbers according to the normalizing method. None: for minmax, maxmin; One for stepup, stepdown; Two for bipiecewiselinear, bipiecewiselinear_percent, see also method",
197    )
198    parser.add_argument("-i", "--infile", help="Input file", type=Path, default="infile.tif")
199    parser.add_argument("-o", "--outfile", help="Output file", type=Path, default="outfile.tif")
200    parser.add_argument(
201        "-m",
202        "--method",
203        help="Method to normalize the input, see also params",
204        type=str,
205        choices=[
206            "minmax",
207            "maxmin",
208            "stepup",
209            "stepdown",
210            "bipiecewiselinear",
211            "bipiecewiselinear_percent",
212            "stepdown_percent",
213            "stepup_percent",
214        ],
215        default="minmax",
216    )
217    parser.add_argument(
218        "-min",
219        "--minimum",
220        help="Minimun value for minmax/maxmin (else the value is calculated from the WHOLE input raster).",
221        type=float,
222    )
223    parser.add_argument(
224        "-max",
225        "--maximum",
226        help="Maximum value for minmax/maxmin (else the value is calculated from the WHOLE input raster).",
227        type=float,
228    )
229    parser.add_argument(
230        "-n",
231        "--NoDataValue",
232        help="output nodata value (send null for default datatype specific, see `from osgeo_utils.gdal_calc import DefaultNDVLookup`)",
233        type=float,
234        nargs="?",
235        default=-9999,
236    )
237    parser.add_argument(
238        "-f",
239        "--format",
240        help="Output format (send null to infer from file, see `from osgeo_utils.auxiliary.util import GetOutputDriverFor`)",
241        type=str,
242        default="GTiff",
243    )
244
245    parser.add_argument(
246        "-t", "--type", help="Output datatype", type=str, default="Float32", choices=list(map(str, GDALDataTypeNames))
247    )
248    parser.add_argument(
249        "-p",
250        "--projwin",
251        nargs=4,
252        type=float,
253        metavar=("min_x", "max_y", "max_x", "min_y"),
254        help="An optional list of 4 coordinates defining the projection window, if not provided the whole raster is calculated",
255    )
256    parser.add_argument(
257        "-r",
258        "--return_dataset",
259        help="Return dataset (for scripting -additional keyword arguments are passed to gdal_calc.Calc) instead of return code",
260        action="store_true",
261    )
262    args = parser.parse_args(argv)
263    args.projwin = tuple(args.projwin) if args.projwin else None
264    if not args.infile.is_file():
265        parser.error("Input file does not exist")
266    if args.format is None:
267        args.format = GetOutputDriverFor(args.outfile)
268    return args
269
270
271def main(argv=None):
272    """
273    <pre><code>
274    minmax: (A-minimum)/(maximum - minimum)
275    maxmin: (A-maximum)/(minimum - maximum)
276    stepup: 0*(A<threshold)+1*(A>=threshold)
277    stepdown: 1*(A<threshold)+0*(A>=threshold)
278    bipiecewiselinear: (A-a)/(b-a) then "0*(A<0)+1*(A>1)"
279    bipiecewiselinear_percent: (A-a*r)/(b*r-a*r) then "0*(A<0)+1*(A>1)"
280    stepup_percent: 0*(A<threshold*r)+1*(A>=threshold*r)
281    stepdown_percent: 1*(A<threshold*r)+0*(A>=threshold*r)
282
283    r : relative delta : data.max() - data.min() / 100
284    </code></pre>
285    """
286    if argv is sys.argv:
287        argv = sys.argv[1:]
288    args = arg_parser(argv)
289
290    print(f"{args=}")
291
292    if args.method == "minmax":
293        del args.params
294        func = lambda minimum, maximum: f"(A-{minimum})/({maximum} - {minimum})"
295        ds = calc(func, **vars(args))
296    elif args.method == "maxmin":
297        del args.params
298        func = lambda minimum, maximum: f"(A-{maximum})/({minimum} - {maximum})"
299        ds = calc(func, **vars(args))
300    elif args.method == "stepup":
301        threshold = args.params[0]
302        del args.params
303        func = lambda threshold: f"0*(A<{threshold})+1*(A>={threshold})"
304        ds = calc(func, **vars(args), threshold=threshold)
305    elif args.method == "stepdown":
306        threshold = args.params[0]
307        del args.params
308        func = lambda threshold: f"1*(A<{threshold})+0*(A>={threshold})"
309        ds = calc(func, **vars(args), threshold=threshold)
310    elif args.method == "bipiecewiselinear":
311        a = args.params[0]
312        b = args.params[1]
313        del args.params
314
315        keep = args.outfile
316        args.outfile = Path(NamedTemporaryFile(suffix=".tif", delete=False).name)
317        print(f"{args=}")
318
319        func = lambda a, b: f"(A-{a})/({b}-{a})"
320        ds = calc(func, **vars(args), a=a, b=b)
321
322        args.infile = args.outfile
323        args.outfile = keep
324        print(f"{args=}")
325
326        func = lambda: "0*(A<0)+1*(A>1)"
327        ds = calc(func, **vars(args))
328    elif args.method == "bipiecewiselinear_percent":
329        """
330        rela_delta = data.max() - data.min() / 100
331        real_a = rela_delta * a
332        real_b = rela_delta * b
333        data = (data - real_a) / (real_b - real_a)
334        """
335        a = args.params[0]
336        b = args.params[1]
337        del args.params
338
339        keep = args.outfile
340        args.outfile = Path(NamedTemporaryFile(suffix=".tif", delete=False).name)
341        print(f"{args=}")
342
343        func = lambda a, b, r: f"(A-{a*r})/({b*r}-{a*r})"
344        ds = calc(func, **vars(args), a=a, b=b, r=0)
345
346        args.infile = args.outfile
347        args.outfile = keep
348        print(f"{args=}")
349
350        func = lambda: "0*(A<0)+1*(A>1)"
351        ds = calc(func, **vars(args))
352    elif args.method == "stepup_percent":
353        threshold = args.params[0]
354        del args.params
355        func = lambda threshold, r: f"0*(A<{threshold*r})+1*(A>={threshold*r})"
356        ds = calc(func, **vars(args), threshold=threshold, r=0)
357    elif args.method == "stepdown_percent":
358        threshold = args.params[0]
359        del args.params
360        func = lambda threshold, r: f"1*(A<{threshold*r})+0*(A>={threshold*r})"
361        ds = calc(func, **vars(args), threshold=threshold, r=0)
362    """
363    elif args.method == "":
364        del args.method
365        func = lambda : f""
366        ds = maxmin(func, **vars(args))
367    """
368
369    if args.return_dataset:
370        return ds
371
372    if isinstance(ds, Dataset):
373        return 0
374    return 1
375
376
377def get_file_minmax(filename, force=True):
378    # try:
379    dataset = Open(filename, GA_ReadOnly)
380    # except RuntimeError as e:
381    #     if "not recognized as a supported file format" in str(e):
382    #         raise FileNotFoundError("not recognized as a supported file format " + filename)
383    if dataset is None:
384        raise FileNotFoundError(filename)
385    raster_band = dataset.GetRasterBand(1)
386    rmin = raster_band.GetMinimum()
387    rmax = raster_band.GetMaximum()
388    if not rmin or not rmax or force:
389        (rmin, rmax) = raster_band.ComputeRasterMinMax(True)
390    return rmin, rmax
391
392
393if __name__ == "__main__":
394    sys.exit(main(sys.argv))
def calc( func, outfile='outfile.tif', infile='infile.tif', band=1, NoDataValue=None, overwrite=True, type='Float32', format='GTiff', projwin=None, minimum=None, maximum=None, threshold=None, a=None, b=None, r=None, **kwargs) -> osgeo.gdal.Dataset:
110def calc(
111    func,
112    outfile="outfile.tif",
113    infile="infile.tif",
114    band=1,
115    NoDataValue=None,
116    overwrite=True,
117    type="Float32",
118    format="GTiff",
119    projwin=None,
120    minimum=None,
121    maximum=None,
122    threshold=None,
123    a=None,
124    b=None,
125    r=None,
126    **kwargs,
127) -> Dataset:
128    """This is the wrapper function for the gdal_calc.Calc utility.
129
130    Although the normalization functions are symbolic strings defined in the main, passed as the `func` argument. Make sure to check them before using this function directly.
131
132    All extra keyword arguments are passed to the gdal_calc.Calc function."""
133    if isinstance(infile, Path):
134        infile = str(infile)
135    if isinstance(outfile, Path):
136        outfile = str(outfile)
137    if "method" in kwargs:
138        if kwargs["method"] in ["minmax", "maxmin", "bipiecewiselinear_percent", "stepup_percent", "stepdown_percent"]:
139            if minimum is None and maximum is None:
140                minimum, maximum = get_file_minmax(infile)
141                print(f"{minimum=}, {maximum=}")
142            if minimum is None:
143                minimum, _ = get_file_minmax(infile)
144                print(f"{minimum=}")
145            if maximum is None:
146                _, maximum = get_file_minmax(infile)
147                print(f"{maximum=}")
148        # drop before passing to Calc
149        del kwargs["method"]
150    # if not projwin:
151    #     info = locals().get("info", read_raster(infile, data=False)[1])
152    #     projwin = get_projwin(info["Transform"], info["RasterXSize"], info["RasterYSize"])
153    #     print(f"{projwin=}")
154
155    # get a the parameter values of the calc function
156    code_obj = func.__code__
157    local_var_names = code_obj.co_varnames[: code_obj.co_nlocals]
158    func_vars = []
159    for var in local_var_names:
160        func_vars += [locals().get(var)]
161    if "r" in local_var_names:
162        func_vars[-1] = (maximum - minimum) / 100
163    print(f"{dict(zip(local_var_names,func_vars))=}")
164    print(f"{local_var_names=}")
165    print(f"{func_vars=}")
166
167    dataset = Calc(
168        calc=func(*func_vars),
169        outfile=outfile,
170        A=infile,
171        A_band=band,
172        NoDataValue=NoDataValue,
173        overwrite=overwrite,
174        type=type,
175        format=format,
176        projwin=projwin,
177        **kwargs,
178    )
179    dataset.FlushCache()
180
181    return dataset

This is the wrapper function for the gdal_calc.Calc utility.

Although the normalization functions are symbolic strings defined in the main, passed as the func argument. Make sure to check them before using this function directly.

All extra keyword arguments are passed to the gdal_calc.Calc function.

def arg_parser(argv=None):
184def arg_parser(argv=None):
185    """Parse arguments list"""
186    from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
187
188    parser = ArgumentParser(
189        description="Raster normalization utility, wrapping on osgeo_utils.gdal_calc with a set of predefined normalization methods. Run `gdal_calc.py --help` for more information.",
190        formatter_class=ArgumentDefaultsHelpFormatter,
191        epilog="documentation at https://fire2a.github.io/fire2a-lib/fire2a/raster/gdal_calc_norm.html",
192    )
193    parser.add_argument(
194        "params",
195        nargs="*",
196        type=float,
197        help="Float numbers according to the normalizing method. None: for minmax, maxmin; One for stepup, stepdown; Two for bipiecewiselinear, bipiecewiselinear_percent, see also method",
198    )
199    parser.add_argument("-i", "--infile", help="Input file", type=Path, default="infile.tif")
200    parser.add_argument("-o", "--outfile", help="Output file", type=Path, default="outfile.tif")
201    parser.add_argument(
202        "-m",
203        "--method",
204        help="Method to normalize the input, see also params",
205        type=str,
206        choices=[
207            "minmax",
208            "maxmin",
209            "stepup",
210            "stepdown",
211            "bipiecewiselinear",
212            "bipiecewiselinear_percent",
213            "stepdown_percent",
214            "stepup_percent",
215        ],
216        default="minmax",
217    )
218    parser.add_argument(
219        "-min",
220        "--minimum",
221        help="Minimun value for minmax/maxmin (else the value is calculated from the WHOLE input raster).",
222        type=float,
223    )
224    parser.add_argument(
225        "-max",
226        "--maximum",
227        help="Maximum value for minmax/maxmin (else the value is calculated from the WHOLE input raster).",
228        type=float,
229    )
230    parser.add_argument(
231        "-n",
232        "--NoDataValue",
233        help="output nodata value (send null for default datatype specific, see `from osgeo_utils.gdal_calc import DefaultNDVLookup`)",
234        type=float,
235        nargs="?",
236        default=-9999,
237    )
238    parser.add_argument(
239        "-f",
240        "--format",
241        help="Output format (send null to infer from file, see `from osgeo_utils.auxiliary.util import GetOutputDriverFor`)",
242        type=str,
243        default="GTiff",
244    )
245
246    parser.add_argument(
247        "-t", "--type", help="Output datatype", type=str, default="Float32", choices=list(map(str, GDALDataTypeNames))
248    )
249    parser.add_argument(
250        "-p",
251        "--projwin",
252        nargs=4,
253        type=float,
254        metavar=("min_x", "max_y", "max_x", "min_y"),
255        help="An optional list of 4 coordinates defining the projection window, if not provided the whole raster is calculated",
256    )
257    parser.add_argument(
258        "-r",
259        "--return_dataset",
260        help="Return dataset (for scripting -additional keyword arguments are passed to gdal_calc.Calc) instead of return code",
261        action="store_true",
262    )
263    args = parser.parse_args(argv)
264    args.projwin = tuple(args.projwin) if args.projwin else None
265    if not args.infile.is_file():
266        parser.error("Input file does not exist")
267    if args.format is None:
268        args.format = GetOutputDriverFor(args.outfile)
269    return args

Parse arguments list

def main(argv=None):
272def main(argv=None):
273    """
274    <pre><code>
275    minmax: (A-minimum)/(maximum - minimum)
276    maxmin: (A-maximum)/(minimum - maximum)
277    stepup: 0*(A<threshold)+1*(A>=threshold)
278    stepdown: 1*(A<threshold)+0*(A>=threshold)
279    bipiecewiselinear: (A-a)/(b-a) then "0*(A<0)+1*(A>1)"
280    bipiecewiselinear_percent: (A-a*r)/(b*r-a*r) then "0*(A<0)+1*(A>1)"
281    stepup_percent: 0*(A<threshold*r)+1*(A>=threshold*r)
282    stepdown_percent: 1*(A<threshold*r)+0*(A>=threshold*r)
283
284    r : relative delta : data.max() - data.min() / 100
285    </code></pre>
286    """
287    if argv is sys.argv:
288        argv = sys.argv[1:]
289    args = arg_parser(argv)
290
291    print(f"{args=}")
292
293    if args.method == "minmax":
294        del args.params
295        func = lambda minimum, maximum: f"(A-{minimum})/({maximum} - {minimum})"
296        ds = calc(func, **vars(args))
297    elif args.method == "maxmin":
298        del args.params
299        func = lambda minimum, maximum: f"(A-{maximum})/({minimum} - {maximum})"
300        ds = calc(func, **vars(args))
301    elif args.method == "stepup":
302        threshold = args.params[0]
303        del args.params
304        func = lambda threshold: f"0*(A<{threshold})+1*(A>={threshold})"
305        ds = calc(func, **vars(args), threshold=threshold)
306    elif args.method == "stepdown":
307        threshold = args.params[0]
308        del args.params
309        func = lambda threshold: f"1*(A<{threshold})+0*(A>={threshold})"
310        ds = calc(func, **vars(args), threshold=threshold)
311    elif args.method == "bipiecewiselinear":
312        a = args.params[0]
313        b = args.params[1]
314        del args.params
315
316        keep = args.outfile
317        args.outfile = Path(NamedTemporaryFile(suffix=".tif", delete=False).name)
318        print(f"{args=}")
319
320        func = lambda a, b: f"(A-{a})/({b}-{a})"
321        ds = calc(func, **vars(args), a=a, b=b)
322
323        args.infile = args.outfile
324        args.outfile = keep
325        print(f"{args=}")
326
327        func = lambda: "0*(A<0)+1*(A>1)"
328        ds = calc(func, **vars(args))
329    elif args.method == "bipiecewiselinear_percent":
330        """
331        rela_delta = data.max() - data.min() / 100
332        real_a = rela_delta * a
333        real_b = rela_delta * b
334        data = (data - real_a) / (real_b - real_a)
335        """
336        a = args.params[0]
337        b = args.params[1]
338        del args.params
339
340        keep = args.outfile
341        args.outfile = Path(NamedTemporaryFile(suffix=".tif", delete=False).name)
342        print(f"{args=}")
343
344        func = lambda a, b, r: f"(A-{a*r})/({b*r}-{a*r})"
345        ds = calc(func, **vars(args), a=a, b=b, r=0)
346
347        args.infile = args.outfile
348        args.outfile = keep
349        print(f"{args=}")
350
351        func = lambda: "0*(A<0)+1*(A>1)"
352        ds = calc(func, **vars(args))
353    elif args.method == "stepup_percent":
354        threshold = args.params[0]
355        del args.params
356        func = lambda threshold, r: f"0*(A<{threshold*r})+1*(A>={threshold*r})"
357        ds = calc(func, **vars(args), threshold=threshold, r=0)
358    elif args.method == "stepdown_percent":
359        threshold = args.params[0]
360        del args.params
361        func = lambda threshold, r: f"1*(A<{threshold*r})+0*(A>={threshold*r})"
362        ds = calc(func, **vars(args), threshold=threshold, r=0)
363    """
364    elif args.method == "":
365        del args.method
366        func = lambda : f""
367        ds = maxmin(func, **vars(args))
368    """
369
370    if args.return_dataset:
371        return ds
372
373    if isinstance(ds, Dataset):
374        return 0
375    return 1

minmax: (A-minimum)/(maximum - minimum)
maxmin: (A-maximum)/(minimum - maximum)
stepup: 0*(A=threshold)
stepdown: 1*(A=threshold)
bipiecewiselinear: (A-a)/(b-a) then "0*(A<0)+1*(A>1)"
bipiecewiselinear_percent: (A-a*r)/(b*r-a*r) then "0*(A<0)+1*(A>1)"
stepup_percent: 0*(A=threshold*r)
stepdown_percent: 1*(A=threshold*r)

r : relative delta : data.max() - data.min() / 100
def get_file_minmax(filename, force=True):
378def get_file_minmax(filename, force=True):
379    # try:
380    dataset = Open(filename, GA_ReadOnly)
381    # except RuntimeError as e:
382    #     if "not recognized as a supported file format" in str(e):
383    #         raise FileNotFoundError("not recognized as a supported file format " + filename)
384    if dataset is None:
385        raise FileNotFoundError(filename)
386    raster_band = dataset.GetRasterBand(1)
387    rmin = raster_band.GetMinimum()
388    rmax = raster_band.GetMaximum()
389    if not rmin or not rmax or force:
390        (rmin, rmax) = raster_band.ComputeRasterMinMax(True)
391    return rmin, rmax