Пример #1
0
def calc(ctx, command, files, output, name, dtype, masked, creation_options):
    """A raster data calculator

    Evaluates an expression using input datasets and writes the result
    to a new dataset.

    Command syntax is lisp-like. An expression consists of an operator
    or function name and one or more strings, numbers, or expressions
    enclosed in parentheses. Functions include ``read`` (gets a raster
    array) and ``asarray`` (makes a 3-D array from 2-D arrays).

    \b
        * (read i) evaluates to the i-th input dataset (a 3-D array).
        * (read i j) evaluates to the j-th band of the i-th dataset (a 2-D
          array).
        * (take foo j) evaluates to the j-th band of a dataset named foo (see
          help on the --name option above).
        * Standard numpy array operators (+, -, *, /) are available.
        * When the final result is a list of arrays, a multi band output
          file is written.
        * When the final result is a single array, a single band output
          file is written.

    Example:

    \b
         $ rio calc "(+ 2 (* 0.95 (read 1)))" tests/data/RGB.byte.tif \\
         > /tmp/out.tif

    Produces a 3-band GeoTIFF with all values scaled by 0.95 and
    incremented by 2.

    \b
        $ rio calc "(asarray (+ 125 (read 1)) (read 1) (read 1))" \\
        > tests/data/shade.tif /tmp/out.tif

    Produces a 3-band RGB GeoTIFF, with red levels incremented by 125,
    from the single-band input.

    """
    import numpy as np

    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
    logger = logging.getLogger('rio')

    try:
        with rasterio.drivers(CPL_DEBUG=verbosity > 2):
            output, files = resolve_inout(files=files, output=output)

            inputs = ([tuple(n.split('=')) for n in name] + [(None, n)
                                                             for n in files])

            with rasterio.open(inputs[0][1]) as first:
                kwargs = first.meta
                kwargs.update(**creation_options)
                kwargs['transform'] = kwargs.pop('affine')
                dtype = dtype or first.meta['dtype']
                kwargs['dtype'] = dtype

            ctxkwds = {}
            for i, (name, path) in enumerate(inputs):
                with rasterio.open(path) as src:
                    # Using the class method instead of instance
                    # method. Latter raises
                    #
                    # TypeError: astype() got an unexpected keyword
                    # argument 'copy'
                    #
                    # possibly something to do with the instance being
                    # a masked array.
                    ctxkwds[name or '_i%d' % (i + 1)] = src.read(masked=masked)

            # Extend snuggs.
            snuggs.func_map['read'] = read_array
            snuggs.func_map['band'] = lambda d, i: get_bands(inputs, d, i)
            snuggs.func_map['bands'] = lambda d: get_bands(inputs, d)
            snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
            snuggs.func_map['sieve'] = lambda *args: sieve(*args)

            res = snuggs.eval(command, **ctxkwds)

            if (isinstance(res, np.ma.core.MaskedArray)
                    and tuple(LooseVersion(np.__version__).version) <
                (1, 9, 0)):
                res = res.filled(kwargs['nodata'])

            if len(res.shape) == 3:
                results = np.ndarray.astype(res, dtype, copy=False)
            else:
                results = np.asanyarray(
                    [np.ndarray.astype(res, dtype, copy=False)])

            kwargs['count'] = results.shape[0]

            with rasterio.open(output, 'w', **kwargs) as dst:
                dst.write(results)

    except snuggs.ExpressionError as err:
        click.echo("Expression Error:")
        click.echo('  %s' % err.text)
        click.echo(' ' + ' ' * err.offset + "^")
        click.echo(err)
        raise click.Abort()
Пример #2
0
def test_partial():
    result = snuggs.eval('((partial * 2) 2)')
    assert result == 4
Пример #3
0
def test_arr_lookup_kwarg_order(ones):
    kwargs = OrderedDict(
        (('foo', ones), ('bar', 2.0 * ones), ('a', 3.0 * ones)))
    r = snuggs.eval('(read 1)', **kwargs)
    assert list(r.flatten()) == [1, 1, 1, 1]
Пример #4
0
def test_int_arr_expr_by_name(ones):
    result = snuggs.eval('(+ (read 1) 1.5)', foo=ones)
    assert list(result.flatten()) == [2.5, 2.5, 2.5, 2.5]
Пример #5
0
def test_negative_decimal():
    """Negative decimals parse correctly"""
    assert snuggs.eval("(< -0.9 0)")
Пример #6
0
def test_int_real_expr():
    assert snuggs.eval('(+ 2 1.1)') == 3.1
Пример #7
0
def test_int_expr():
    assert snuggs.eval('(+ 1 2)') == 3
Пример #8
0
def test_missing_func2():
    with pytest.raises(SyntaxError) as excinfo:
        snuggs.eval("(# 1 2)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 2
Пример #9
0
def test_masked_arr():
    foo = numpy.ma.masked_equal(numpy.array([0, 0, 0, 1], dtype='uint8'), 0)
    r = snuggs.eval('(+ foo 1)', foo=foo)
    assert list(r.data.flatten()) == [0, 0, 0, 2]
    assert list(r.flatten()) == [numpy.ma.masked, numpy.ma.masked, numpy.ma.masked, 2]
Пример #10
0
def test_missing_closing_paren():
    with pytest.raises(SyntaxError) as excinfo:
        result = snuggs.eval("(+ 1 2")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 7
    assert str(excinfo.value) == 'Expected ")"'
Пример #11
0
def test_nil():
    assert snuggs.eval('(== nil nil)')
    assert not snuggs.eval('(== 1 nil)')
    assert not snuggs.eval('(== nil 1)')
    assert snuggs.eval('(!= 1 nil)')
    assert snuggs.eval('(!= nil 1)')
Пример #12
0
def test_map_asarray():
    result = snuggs.eval('(asarray (map (partial * 2) (asarray 1 2 3)))')
    assert list(result) == [2, 4, 6]
Пример #13
0
def test_map_func():
    result = snuggs.eval('(map sqrt (asarray 1 4 9))')
    assert list(result) == [1, 2, 3]
Пример #14
0
def test_nil():
    assert snuggs.eval('(== nil nil)')
    assert not snuggs.eval('(== 1 nil)')
    assert not snuggs.eval('(== nil 1)')
    assert snuggs.eval('(!= 1 nil)')
    assert snuggs.eval('(!= nil 1)')
Пример #15
0
def test_missing_func2():
    with pytest.raises(SyntaxError) as excinfo:
        result = snuggs.eval("(# 1 2)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 2
    assert str(excinfo.value) == "expected a function or operator"
Пример #16
0
def test_missing_closing_paren():
    with pytest.raises(SyntaxError) as excinfo:
        snuggs.eval("(+ 1 2")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 7
Пример #17
0
def test_undefined_var():
    with pytest.raises(SyntaxError) as excinfo:
        result = snuggs.eval("(+ 1 bogus)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 6
    assert str(excinfo.value) == "name 'bogus' is not defined"
Пример #18
0
def test_bogus_higher_order_func():
    with pytest.raises(SyntaxError) as excinfo:
        snuggs.eval("((bogus * 2) 2)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 3
Пример #19
0
def test_bogus_higher_order_func():
    with pytest.raises(SyntaxError) as excinfo:
        result = snuggs.eval("((bogus * 2) 2)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 3
    assert str(excinfo.value) == "expected a function or operator"
Пример #20
0
def test_int_mult_expr():
    assert snuggs.eval('(+ 1 2 3)') == 6
Пример #21
0
def test_type_error():
    with pytest.raises(TypeError) as excinfo:
        result = snuggs.eval("(+ 1 'bogus')")
Пример #22
0
def test_arr_var(ones):
    r = snuggs.eval('(+ foo 0)', foo=ones)
    assert list(r.flatten()) == [1, 1, 1, 1]
Пример #23
0
def test_real_expr():
    assert round(snuggs.eval('(* 0.1 0.2)'), 3) == 0.02
Пример #24
0
def test_arr_take(ones):
    r = snuggs.eval('(take foo 1)', foo=ones)
    assert list(r.flatten()) == [1, 1]
    r = snuggs.eval('(take foo 2)', foo=ones)
    assert list(r.flatten()) == [1, 1]
Пример #25
0
def test_int_real_expr():
    assert snuggs.eval('(+ 2 1.1)') == 3.1
Пример #26
0
def image_composite(inputs, algo, output, oformat, vza, mask_band, mask_val):
    """ Create image composites based on some criteria
    Output image composites retain original values from input images that meet
    a certain criteria. For example, in a maximum NDVI composite with 10 input
    images, all bands for a given pixel will contain the band values from the
    input raster that had the highest NDVI value.
    Users can choose from a set of predefined compositing algorithms or may
    specify an Snuggs S-expression that defines the compositing criteria.
    Normalized Differenced indexes can be computed using "(normdiff a b)" for
    the Normalized Difference between "a" and "b" (or "nir" and "red").
    See https://github.com/mapbox/snuggs for more information on Snuggs
    expressions.
    The indexes for common optical bands (e.g., red, nir, blue) within the
    input rasters are included as optional arguments and are indexed in
    wavelength sequential order. You may need to overwrite the default indexes
    of bands used in a given S-expression with the correct band index.
    Additional bands may be identified and indexed using the
    '--band NAME=INDEX' option.
    Currently, input images must be "stacked", meaning that they contain the
    same bands and are the same shape and extent.
    Example:
    1. Create a composite based on maximum NDVI
        Use the built-in maxNDVI algorithm:
        \b
        $ image_composite.py --algo maxNDVI image1.gtif image2.gtif image3.gtif
            composite_maxNDVI.gtif
        or with S-expression:
        \b
        $ image_composite.py --expr '(max (/ (- nir red) (+ nir red)))'
            image1.gtif image2.gtif image3.gtif composite_maxNDVI.gtif
        or with S-expressions using the normdiff shortcut:
        \b
        $ image_composite.py --expr '(max (normdiff nir red))'
            image1.gtif image2.gtif image3.gtif composite_maxNDVI.gtif
    2. Create a composite based on median EVI (not recommended)
        With S-expression:
        \b
        $ evi='(median (/ (- nir red) (+ (- (+ nir (* 6 red)) (* 7.5 blue)) 1)))'
        $ image_composite.py --expr "$evi"  image1.gtif image2.gtif image3.gtif
            composite_medianEVI.gtif
    3. Create a composite based on median NBR
        With S-expression:
        \b
        $ image_composite.py --expr '(median (normdiff nir sswir))'
            image1.gtif image2.gtif image3.gtif composite_maxNBR.gtif
    """
    verbose = True
    if verbose:
        logger.setLevel(logging.DEBUG)
    elif quiet:
        logger.setLevel(logging.ERROR)

        expr = _ALGO[algo]
    if algo is not None:
        logger.debug('Using predefined algorithm: {}'.format(algo))
        expr = _ALGO[algo]

    # Setup band keywords
    _bands = {'vza': vza}

    # Find only the band names and indexes required for the composite criteria
    crit_indices = {k: v - 1 for k, v in _bands.iteritems() if k in expr}

    # Enhance snuggs expressions to return index of value matching function
    snuggs.func_map['max'] = lambda a: np.argmax(a, axis=0)
    snuggs.func_map['min'] = lambda a: np.argmin(a, axis=0)
    snuggs.func_map['median'] = lambda a: np.argmin(
        np.abs(a - np.median(a, axis=0)), axis=0)
    snuggs.func_map['normdiff'] = lambda a, b: snuggs.eval(
        '(/ (- a b) (+ a b))', **{
            'a': a,
            'b': b
        })

    with rasterio.drivers():

        # Read in the first image to fetch metadata
        with rasterio.open(inputs[0]) as first:
            meta = first.meta
            if 'transform' in meta:
                meta.pop('transform')  # remove transform since deprecated
            meta.update(driver=oformat)
            if len(set(first.block_shapes)) != 1:
                click.echo('Cannot process input files - '
                           'All bands must have same block shapes')
                raise click.Abort()
            block_nrow, block_ncol = first.block_shapes[0]
            windows = first.block_windows(1)
            n_windows = math.ceil(meta['height'] / block_nrow * meta['width'] /
                                  block_ncol)

            # Ensure mask_band exists, if specified
            if mask_band:
                if mask_band <= meta['count'] and mask_band > 0:
                    mask_band -= 1
                else:
                    click.echo('Mask band does not exist in INPUT images')
                    raise click.Abort()

        # Initialize output data and create composite
        with rasterio.open(output, 'w', **meta) as dst:
            # Process by block
            dat = np.ma.empty(
                (len(inputs), meta['count'], block_nrow, block_ncol),
                dtype=np.dtype(meta['dtype']))
            mi, mj = np.meshgrid(np.arange(block_nrow),
                                 np.arange(block_ncol),
                                 indexing='ij')
            # Open all source files one time
            srcs = [rasterio.open(fname) for fname in inputs]

            logger.debug('Processing blocks')
            if _has_progressbar:
                widgets = [
                    progressbar.Percentage(),
                    progressbar.BouncingBar(
                        marker=progressbar.RotatingMarker())
                ]
                pbar = progressbar.ProgressBar(widgets=widgets).start()

            for i, (idx, window) in enumerate(windows):
                # Update dat and mi, mj only if window changes
                nrow = window[0][1] - window[0][0]
                ncol = window[1][1] - window[1][0]
                if dat.shape[-2] != nrow or dat.shape[-1] != ncol:
                    dat = np.ma.empty((len(inputs), meta['count'], nrow, ncol),
                                      dtype=np.dtype(meta['dtype']))
                    mi, mj = np.meshgrid(np.arange(nrow),
                                         np.arange(ncol),
                                         indexing='ij')
                for j, src in enumerate(srcs):
                    dat[j, ...] = src.read(masked=True, window=window)
                    # Mask values matching mask_vals if mask_band
                    if mask_band and mask_val:
                        dat[j, ...].mask = np.logical_or(
                            dat[j, ...].mask,
                            np.in1d(
                                dat[j, mask_band, ...],
                                mask_val,
                            ).reshape(dat.shape[-2], dat.shape[-1]))

                # Find indices of files for composite
                crit = {k: dat[:, v, ...] for k, v in crit_indices.iteritems()}
                crit_idx = snuggs.eval(expr, **crit)

                # Create output composite
                # Use np.rollaxis to get (nimage, nrow, ncol, nband) shape
                composite = np.rollaxis(dat, 1, 4)[crit_idx, mi, mj]

                # Write out
                for i_b in range(composite.shape[-1]):
                    dst.write(composite[:, :, i_b],
                              indexes=i_b + 1,
                              window=window)
                if _has_progressbar:
                    pbar.update(int((i + 1) / n_windows * 100))
Пример #27
0
def test_real_int_expr():
    assert snuggs.eval('(+ 1.1 2)') == 3.1
Пример #28
0
def calc(ctx, command, files, name, dtype):
    """A raster data calculator

    Evaluates an expression using input datasets and writes the result
    to a new dataset.

    Command syntax is lisp-like. An expression consists of an operator
    or function name and one or more strings, numbers, or expressions
    enclosed in parentheses. Functions include ``read`` (gets a raster
    array) and ``asarray`` (makes a 3-D array from 2-D arrays).

    \b
        * (read i) evaluates to the i-th input dataset (a 3-D array).
        * (read i j) evaluates to the j-th band of the i-th dataset (a 2-D
          array).
        * (take foo j) evaluates to the j-th band of a dataset named foo (see
          help on the --name option above).
        * Standard numpy array operators (+, -, *, /) are available.
        * When the final result is a list of arrays, a multi band output
          file is written.
        * When the final result is a single array, a single band output
          file is written.

    Example:

    \b
         $ rio calc "(+ 2 (* 0.95 (read 1)))" tests/data/RGB.byte.tif \\
         > /tmp/out.tif

    Produces a 3-band GeoTIFF with all values scaled by 0.95 and
    incremented by 2.

    \b
        $ rio calc "(asarray (+ 125 (read 1)) (read 1) (read 1))" \\
        > tests/data/shade.tif /tmp/out.tif

    Produces a 3-band RGB GeoTIFF, with red levels incremented by 125,
    from the single-band input.

    """
    import numpy as np

    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
    logger = logging.getLogger('rio')

    try:
        with rasterio.drivers(CPL_DEBUG=verbosity > 2):
            output = files[-1]

            inputs = ([tuple(n.split('=')) for n in name] +
                      [(None, n) for n in files[:-1]])

            with rasterio.open(inputs[0][1]) as first:
                kwargs = first.meta
                kwargs['transform'] = kwargs.pop('affine')
                dtype = dtype or first.meta['dtype']
                kwargs['dtype'] = dtype

            ctxkwds = {}
            for i, (name, path) in enumerate(inputs):
                with rasterio.open(path) as src:
                    # Using the class method instead of instance
                    # method. Latter raises
                    #
                    # TypeError: astype() got an unexpected keyword
                    # argument 'copy'
                    #
                    # possibly something to do with the instance being
                    # a masked array.
                    ctxkwds[name or '_i%d' % (i+1)] = src.read()

            # Extend snuggs.
            snuggs.func_map['read'] = read_array
            snuggs.func_map['band'] = lambda d, i: get_bands(inputs, d, i)
            snuggs.func_map['bands'] = lambda d: get_bands(inputs, d)
            snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
            snuggs.func_map['sieve'] = lambda *args: sieve(*args)

            res = snuggs.eval(command, **ctxkwds)

            if len(res.shape) == 3:
                results = np.ndarray.astype(res, dtype, copy=False)
            else:
                results = np.asanyarray(
                    [np.ndarray.astype(res, dtype, copy=False)])

            kwargs['count'] = results.shape[0]

            with rasterio.open(output, 'w', **kwargs) as dst:
                dst.write(results)

        sys.exit(0)
    except snuggs.ExpressionError as err:
        click.echo("Expression Error:")
        click.echo('  %s' % err.text)
        click.echo(' ' +  ' ' * err.offset + "^")
        click.echo(err)
        sys.exit(1)
    except Exception as err:
        t, v, tb = sys.exc_info()
        for line in traceback.format_exception_only(t, v):
            click.echo(line, nl=False)
        sys.exit(1)
Пример #29
0
def test_arr_var(ones):
    r = snuggs.eval('(+ foo 0)', foo=ones)
    assert list(r.flatten()) == [1, 1, 1, 1]
Пример #30
0
def test_multi_operator_array(ones):
    result = snuggs.eval('(+ ones (/ ones 1 0.5) (* ones 1 3))', ones=ones)
    assert list(result.flatten()) == [6.0] * 4
Пример #31
0
def test_arr_lookup_2(ones):
    r = snuggs.eval('(read 1 1)', foo=ones)
    assert list(r.flatten()) == [1, 1]
Пример #32
0
def test_masked_arr():
    foo = numpy.ma.masked_equal(numpy.array([0, 0, 0, 1], dtype='uint8'), 0)
    r = snuggs.eval('(+ foo 1)', foo=foo)
    assert list(r.data.flatten()) == [0, 0, 0, 2]
    assert list(
        r.flatten()) == [numpy.ma.masked, numpy.ma.masked, numpy.ma.masked, 2]
Пример #33
0
def test_arr_take(ones):
    r = snuggs.eval('(take foo 1)', foo=ones)
    assert list(r.flatten()) == [1, 1]
    r = snuggs.eval('(take foo 2)', foo=ones)
    assert list(r.flatten()) == [1, 1]
Пример #34
0
def test_missing_func():
    with pytest.raises(SyntaxError) as excinfo:
        snuggs.eval("(0 1 2)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 2
    assert str(excinfo.value) == "'0' is not a function or operator"
Пример #35
0
def test_int_arr_expr(ones):
    result = snuggs.eval('(+ foo 1)', foo=ones)
    assert list(result.flatten()) == [2, 2, 2, 2]
Пример #36
0
def test_undefined_var():
    with pytest.raises(SyntaxError) as excinfo:
        snuggs.eval("(+ 1 bogus)")
    assert excinfo.value.lineno == 1
    assert excinfo.value.offset == 6
    assert str(excinfo.value) == "name 'bogus' is not defined"
Пример #37
0
def test_int_arr_read(ones):
    result = snuggs.eval('(+ (read 1 1) 1.5)', foo=ones)
    assert list(result.flatten()) == [2.5, 2.5]
Пример #38
0
def test_type_error():
    with pytest.raises(TypeError):
        snuggs.eval("(+ 1 'bogus')")
Пример #39
0
def test_list(ones):
    result = snuggs.eval(
        '(asarray (take foo 1) (take foo 1) (take bar 1) (take bar 1))',
        foo=ones, bar=ones)
    assert list(result.flatten()) == [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
Пример #40
0
def test_int_expr():
    assert snuggs.eval('(+ 1 2)') == 3
Пример #41
0
def test_eq(ones):
    ones[0][0] = 2
    result = snuggs.eval('(== foo 1)', foo=ones)
    assert list(result.flatten()) == [False, True, True, True]
Пример #42
0
def test_real_expr():
    assert round(snuggs.eval('(* 0.1 0.2)'), 3) == 0.02
Пример #43
0
def test_and(truetrue, truefalse):
    result = snuggs.eval(
        '(& foo bar)', foo=truetrue, bar=truefalse)
    assert list(result.flatten()) == [True, False]
Пример #44
0
def test_real_int_expr():
    assert snuggs.eval('(+ 1.1 2)') == 3.1
Пример #45
0
def test_ones_like(truefalse):
    result = snuggs.eval("(ones_like foo 'uint8')", foo=truefalse)
    assert list(result.flatten()) == [1.0, 1.0]
Пример #46
0
def test_arr_var_long(ones):
    r = snuggs.eval('(+ FOO_BAR_42 0)', FOO_BAR_42=ones)
    assert list(r.flatten()) == [1, 1, 1, 1]
Пример #47
0
def test_full_like(truefalse):
    result = snuggs.eval("(full_like foo 3.14 'float64')", foo=truefalse)
    assert list(result.flatten()) == [3.14, 3.14]
    result = snuggs.eval('(full_like foo 3.14 "float64")', foo=truefalse)
    assert list(result.flatten()) == [3.14, 3.14]
Пример #48
0
def test_arr_lookup_2(ones):
    r = snuggs.eval('(read 1 1)', foo=ones)
    assert list(r.flatten()) == [1, 1]
Пример #49
0
def test_full_like(truefalse):
    result = snuggs.eval("(full_like foo 3.14 'float64')", foo=truefalse)
    assert list(result.flatten()) == [3.14, 3.14]
    result = snuggs.eval('(full_like foo 3.14 "float64")', foo=truefalse)
    assert list(result.flatten()) == [3.14, 3.14]
Пример #50
0
def test_int_arr_expr(ones):
    result = snuggs.eval('(+ foo 1)', foo=ones)
    assert list(result.flatten()) == [2, 2, 2, 2]
Пример #51
0
def test_ufunc(truetrue, truefalse):
    result = snuggs.eval('(where (& foo bar) 1 0)',
                         foo=truetrue,
                         bar=truefalse)
    assert list(result.flatten()) == [1.0, 0.0]
Пример #52
0
def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit, creation_options):
    """A raster data calculator

    Evaluates an expression using input datasets and writes the result
    to a new dataset.

    Command syntax is lisp-like. An expression consists of an operator
    or function name and one or more strings, numbers, or expressions
    enclosed in parentheses. Functions include ``read`` (gets a raster
    array) and ``asarray`` (makes a 3-D array from 2-D arrays).

    \b
        * (read i) evaluates to the i-th input dataset (a 3-D array).
        * (read i j) evaluates to the j-th band of the i-th dataset (a
          2-D array).
        * (take foo j) evaluates to the j-th band of a dataset named foo
          (see help on the --name option above).
        * Standard numpy array operators (+, -, *, /) are available.
        * When the final result is a list of arrays, a multiple band
          output file is written.
        * When the final result is a single array, a single band output
          file is written.

    Example:

    \b
         $ rio calc "(+ 2 (* 0.95 (read 1)))" tests/data/RGB.byte.tif \\
         > /tmp/out.tif

    The command above produces a 3-band GeoTIFF with all values scaled
    by 0.95 and incremented by 2.

    \b
        $ rio calc "(asarray (+ 125 (read 1)) (read 1) (read 1))" \\
        > tests/data/shade.tif /tmp/out.tif

    The command above produces a 3-band RGB GeoTIFF, with red levels
    incremented by 125, from the single-band input.

    The maximum amount of memory used to perform caculations defaults to
    64 MB. This number can be increased to improve speed of calculation.

    """
    import numpy as np

    try:
        with ctx.obj['env']:
            output, files = resolve_inout(files=files, output=output,
                                          overwrite=overwrite)
            inputs = ([tuple(n.split('=')) for n in name] +
                      [(None, n) for n in files])
            sources = [rasterio.open(path) for name, path in inputs]

            first = sources[0]
            kwargs = first.profile
            kwargs.update(**creation_options)
            dtype = dtype or first.meta['dtype']
            kwargs['dtype'] = dtype

            # Extend snuggs.
            snuggs.func_map['read'] = _read_array
            snuggs.func_map['band'] = lambda d, i: _get_bands(inputs, sources, d, i)
            snuggs.func_map['bands'] = lambda d: _get_bands(inputs, sources, d)
            snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
            snuggs.func_map['sieve'] = lambda *args: sieve(*args)

            dst = None

            # The windows iterator is initialized with a single sample.
            # The actual work windows will be added in the second
            # iteration of the loop.
            work_windows = [(None, Window(0, 0, 16, 16))]

            for ij, window in work_windows:

                ctxkwds = OrderedDict()

                for i, ((name, path), src) in enumerate(zip(inputs, sources)):

                    # Using the class method instead of instance
                    # method. Latter raises
                    #
                    # TypeError: astype() got an unexpected keyword
                    # argument 'copy'
                    #
                    # possibly something to do with the instance being
                    # a masked array.
                    ctxkwds[name or '_i%d' % (i + 1)] = src.read(masked=masked, window=window)

                res = snuggs.eval(command, **ctxkwds)

                if (isinstance(res, np.ma.core.MaskedArray) and (
                        tuple(LooseVersion(np.__version__).version) < (1, 9) or
                        tuple(LooseVersion(np.__version__).version) > (1, 10))):
                    res = res.filled(kwargs['nodata'])

                if len(res.shape) == 3:
                    results = np.ndarray.astype(res, dtype, copy=False)
                else:
                    results = np.asanyarray(
                        [np.ndarray.astype(res, dtype, copy=False)])

                # The first iteration is only to get sample results and from them
                # compute some properties of the output dataset.
                if dst is None:
                    kwargs['count'] = results.shape[0]
                    dst = rasterio.open(output, 'w', **kwargs)
                    work_windows.extend(_chunk_output(dst.width, dst.height, dst.count, np.dtype(dst.dtypes[0]).itemsize, mem_limit=mem_limit))

                # In subsequent iterations we write results.
                else:
                    dst.write(results, window=window)

    except snuggs.ExpressionError as err:
        click.echo("Expression Error:")
        click.echo('  %s' % err.text)
        click.echo(' ' + ' ' * err.offset + "^")
        click.echo(err)
        raise click.Abort()

    finally:
        if dst:
            dst.close()
        for src in sources:
            src.close()
Пример #53
0
def test_partial():
    result = snuggs.eval('((partial * 2) 2)')
    assert result == 4
Пример #54
0
def image_composite(inputs, algo, expr, output, oformat, creation_options,
                    blue, green, red, nir, fswir, sswir, band,
                    mask_band, mask_val,
                    verbose, quiet):
    """ Create image composites based on some criteria

    Output image composites retain original values from input images that meet
    a certain criteria. For example, in a maximum NDVI composite with 10 input
    images, all bands for a given pixel will contain the band values from the
    input raster that had the highest NDVI value.

    Users can choose from a set of predefined compositing algorithms or may
    specify an Snuggs S-expression that defines the compositing criteria.
    Normalized Differenced indexes can be computed using "(normdiff a b)" for
    the Normalized Difference between "a" and "b" (or "nir" and "red").
    See https://github.com/mapbox/snuggs for more information on Snuggs
    expressions.

    The indexes for common optical bands (e.g., red, nir, blue) within the
    input rasters are included as optional arguments and are indexed in
    wavelength sequential order. You may need to overwrite the default indexes
    of bands used in a given S-expression with the correct band index.
    Additional bands may be identified and indexed using the
    '--band NAME=INDEX' option.

    Currently, input images must be "stacked", meaning that they contain the
    same bands and are the same shape and extent.

    Example:

    1. Create a composite based on maximum NDVI

        Use the built-in maxNDVI algorithm:

        \b
        $ image_composite.py --algo maxNDVI image1.gtif image2.gtif image3.gtif
            composite_maxNDVI.gtif

        or with S-expression:

        \b
        $ image_composite.py --expr '(max (/ (- nir red) (+ nir red)))'
            image1.gtif image2.gtif image3.gtif composite_maxNDVI.gtif

        or with S-expressions using the normdiff shortcut:

        \b
        $ image_composite.py --expr '(max (normdiff nir red))'
            image1.gtif image2.gtif image3.gtif composite_maxNDVI.gtif

    2. Create a composite based on median EVI (not recommended)

        With S-expression:

        \b
        $ evi='(median (/ (- nir red) (+ (- (+ nir (* 6 red)) (* 7.5 blue)) 1)))'
        $ image_composite.py --expr "$evi"  image1.gtif image2.gtif image3.gtif
            composite_medianEVI.gtif

    3. Create a composite based on median NBR

        With S-expression:

        \b
        $ image_composite.py --expr '(median (normdiff nir sswir))'
            image1.gtif image2.gtif image3.gtif composite_maxNBR.gtif

    """
    if verbose:
        logger.setLevel(logging.DEBUG)
    elif quiet:
        logger.setLevel(logging.ERROR)

    # Prefer built-in algorithms to expressions if both are specified
    if not algo and not expr:
        raise click.UsageError('Error: must specify either --algo or --expr')
    elif algo is not None and expr is not None:
        logger.warning('Predefined algorithm and expression both defined. '
                       'Composite will be generated with predefined algorithm')
        expr = _ALGO[algo]
    elif algo is not None:
        logger.debug('Using predefined algorithm: {}'.format(algo))
        expr = _ALGO[algo]

    if not quiet:
        click.echo('Compositing criteria S-expression: "{}"'.format(expr))

    # Setup band keywords
    _bands = {'blue': blue, 'green': green, 'red': red,
              'nir': nir, 'fswir': fswir, 'sswir': sswir}
    # Parse any additional, user specified bands
    if band:
        for k, v in band.iteritems():
            try:
                _bands.update({k: int(v)})
            except ValueError:
                raise click.BadParameter(
                    'Value specified as KEY=VAL pair in --band must be an int')

    # Find only the band names and indexes required for the composite criteria
    crit_indices = {k: v - 1 for k, v in _bands.iteritems() if k in expr}

    # Enhance snuggs expressions to return index of value matching function
    snuggs.func_map['max'] = lambda a: np.argmax(a, axis=0)
    snuggs.func_map['min'] = lambda a: np.argmin(a, axis=0)
    snuggs.func_map['median'] = lambda a: np.argmin(
        np.abs(a - np.median(a, axis=0)), axis=0)
    snuggs.func_map['normdiff'] = lambda a, b: snuggs.eval(
        '(/ (- a b) (+ a b))', **{'a':a, 'b':b})

    with rasterio.drivers():

        # Read in the first image to fetch metadata
        with rasterio.open(inputs[0]) as first:
            meta = first.meta
            if 'transform' in meta:
                meta.pop('transform')  # remove transform since deprecated
            meta.update(driver=oformat)
            meta.update(**creation_options)
            if len(set(first.block_shapes)) != 1:
                click.echo('Cannot process input files - '
                           'All bands must have same block shapes')
                raise click.Abort()
            block_nrow, block_ncol = first.block_shapes[0]
            windows = first.block_windows(1)
            n_windows = math.ceil(meta['height'] / block_nrow *
                                  meta['width'] / block_ncol)

            # Ensure mask_band exists, if specified
            if mask_band:
                if mask_band <= meta['count'] and mask_band > 0:
                    mask_band -= 1
                else:
                    click.echo('Mask band does not exist in INPUT images')
                    raise click.Abort()

        # Initialize output data and create composite
        with rasterio.open(output, 'w', **meta) as dst:
            # Process by block
            dat = np.ma.empty((len(inputs), meta['count'],
                               block_nrow, block_ncol),
                              dtype=np.dtype(meta['dtype']))
            mi, mj = np.meshgrid(np.arange(block_nrow), np.arange(block_ncol),
                                 indexing='ij')
            # Open all source files one time
            srcs = [rasterio.open(fname) for fname in inputs]

            logger.debug('Processing blocks')
            if _has_progressbar and not quiet:
                widgets = [
                    progressbar.Percentage(),
                    progressbar.BouncingBar(
                        marker=progressbar.RotatingMarker())
                ]
                pbar = progressbar.ProgressBar(widgets=widgets).start()

            for i, (idx, window) in enumerate(windows):
                # Update dat and mi, mj only if window changes
                nrow = window[0][1] - window[0][0]
                ncol = window[1][1] - window[1][0]
                if dat.shape[-2] != nrow or dat.shape[-1] != ncol:
                    dat = np.ma.empty((len(inputs), meta['count'],
                                       nrow, ncol),
                                      dtype=np.dtype(meta['dtype']))
                    mi, mj = np.meshgrid(np.arange(nrow), np.arange(ncol),
                                         indexing='ij')
                for j, src in enumerate(srcs):
                    dat[j, ...] = src.read(masked=True, window=window)
                    # Mask values matching mask_vals if mask_band
                    if mask_band and mask_val:
                        dat[j, ...].mask = np.logical_or(
                            dat[j, ...].mask,
                            np.in1d(dat[j, mask_band, ...], mask_val,).reshape(
                                dat.shape[-2], dat.shape[-1])
                        )

                # Find indices of files for composite
                crit = {k: dat[:, v, ...] for k, v in crit_indices.iteritems()}
                crit_idx = snuggs.eval(expr, **crit)

                # Create output composite
                # Use np.rollaxis to get (nimage, nrow, ncol, nband) shape
                composite = np.rollaxis(dat, 1, 4)[crit_idx, mi, mj]

                # Write out
                for i_b in range(composite.shape[-1]):
                    dst.write(composite[:, :, i_b], indexes=i_b + 1,
                              window=window)
                if not quiet and _has_progressbar:
                    pbar.update(int((i + 1) / n_windows * 100))
Пример #55
0
def test_map_func():
    result = snuggs.eval('(map sqrt (asarray 1 4 9))')
    assert list(result) == [1, 2, 3]
Пример #56
0
def calc(ctx, command, files, output, name, dtype, masked, overwrite,
         creation_options):
    """A raster data calculator

    Evaluates an expression using input datasets and writes the result
    to a new dataset.

    Command syntax is lisp-like. An expression consists of an operator
    or function name and one or more strings, numbers, or expressions
    enclosed in parentheses. Functions include ``read`` (gets a raster
    array) and ``asarray`` (makes a 3-D array from 2-D arrays).

    \b
        * (read i) evaluates to the i-th input dataset (a 3-D array).
        * (read i j) evaluates to the j-th band of the i-th dataset (a 2-D
          array).
        * (take foo j) evaluates to the j-th band of a dataset named foo (see
          help on the --name option above).
        * Standard numpy array operators (+, -, *, /) are available.
        * When the final result is a list of arrays, a multi band output
          file is written.
        * When the final result is a single array, a single band output
          file is written.

    Example:

    \b
         $ rio calc "(+ 2 (* 0.95 (read 1)))" tests/data/RGB.byte.tif \\
         > /tmp/out.tif

    Produces a 3-band GeoTIFF with all values scaled by 0.95 and
    incremented by 2.

    \b
        $ rio calc "(asarray (+ 125 (read 1)) (read 1) (read 1))" \\
        > tests/data/shade.tif /tmp/out.tif

    Produces a 3-band RGB GeoTIFF, with red levels incremented by 125,
    from the single-band input.

    """
    import numpy as np

    try:
        with ctx.obj['env']:
            output, files = resolve_inout(files=files, output=output,
                                          overwrite=overwrite)

            inputs = ([tuple(n.split('=')) for n in name] +
                      [(None, n) for n in files])

            with rasterio.open(inputs[0][1]) as first:
                kwargs = first.meta
                kwargs.update(**creation_options)
                dtype = dtype or first.meta['dtype']
                kwargs['dtype'] = dtype

            ctxkwds = OrderedDict()
            for i, (name, path) in enumerate(inputs):
                with rasterio.open(path) as src:
                    # Using the class method instead of instance
                    # method. Latter raises
                    #
                    # TypeError: astype() got an unexpected keyword
                    # argument 'copy'
                    #
                    # possibly something to do with the instance being
                    # a masked array.
                    ctxkwds[name or '_i%d' % (i + 1)] = src.read(masked=masked)

            # Extend snuggs.
            snuggs.func_map['read'] = read_array
            snuggs.func_map['band'] = lambda d, i: get_bands(inputs, d, i)
            snuggs.func_map['bands'] = lambda d: get_bands(inputs, d)
            snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
            snuggs.func_map['sieve'] = lambda *args: sieve(*args)

            res = snuggs.eval(command, ctxkwds)

            if (isinstance(res, np.ma.core.MaskedArray) and (
                    tuple(LooseVersion(np.__version__).version) < (1, 9) or
                    tuple(LooseVersion(np.__version__).version) > (1, 10))):
                res = res.filled(kwargs['nodata'])

            if len(res.shape) == 3:
                results = np.ndarray.astype(res, dtype, copy=False)
            else:
                results = np.asanyarray(
                    [np.ndarray.astype(res, dtype, copy=False)])

            kwargs['count'] = results.shape[0]

            with rasterio.open(output, 'w', **kwargs) as dst:
                dst.write(results)

    except snuggs.ExpressionError as err:
        click.echo("Expression Error:")
        click.echo('  %s' % err.text)
        click.echo(' ' + ' ' * err.offset + "^")
        click.echo(err)
        raise click.Abort()
Пример #57
0
def test_map_asarray():
    result = snuggs.eval('(asarray (map (partial * 2) (asarray 1 2 3)))')
    assert list(result) == [2, 4, 6]
Пример #58
0
def calc(ctx, command, files, output, name, dtype, masked, overwrite,
         mem_limit, creation_options):
    """A raster data calculator

    Evaluates an expression using input datasets and writes the result
    to a new dataset.

    Command syntax is lisp-like. An expression consists of an operator
    or function name and one or more strings, numbers, or expressions
    enclosed in parentheses. Functions include ``read`` (gets a raster
    array) and ``asarray`` (makes a 3-D array from 2-D arrays).

    \b
        * (read i) evaluates to the i-th input dataset (a 3-D array).
        * (read i j) evaluates to the j-th band of the i-th dataset (a
          2-D array).
        * (take foo j) evaluates to the j-th band of a dataset named foo
          (see help on the --name option above).
        * Standard numpy array operators (+, -, *, /) are available.
        * When the final result is a list of arrays, a multiple band
          output file is written.
        * When the final result is a single array, a single band output
          file is written.

    Example:

    \b
         $ rio calc "(+ 2 (* 0.95 (read 1)))" tests/data/RGB.byte.tif \\
         > /tmp/out.tif

    The command above produces a 3-band GeoTIFF with all values scaled
    by 0.95 and incremented by 2.

    \b
        $ rio calc "(asarray (+ 125 (read 1)) (read 1) (read 1))" \\
        > tests/data/shade.tif /tmp/out.tif

    The command above produces a 3-band RGB GeoTIFF, with red levels
    incremented by 125, from the single-band input.

    The maximum amount of memory used to perform caculations defaults to
    64 MB. This number can be increased to improve speed of calculation.

    """
    import numpy as np

    dst = None
    sources = []

    try:
        with ctx.obj['env']:
            output, files = resolve_inout(files=files,
                                          output=output,
                                          overwrite=overwrite)
            inputs = ([tuple(n.split('=')) for n in name] + [(None, n)
                                                             for n in files])
            sources = [rasterio.open(path) for name, path in inputs]

            first = sources[0]
            kwargs = first.profile
            kwargs.update(**creation_options)
            dtype = dtype or first.meta['dtype']
            kwargs['dtype'] = dtype

            # Extend snuggs.
            snuggs.func_map['read'] = _read_array
            snuggs.func_map['band'] = lambda d, i: _get_bands(
                inputs, sources, d, i)
            snuggs.func_map['bands'] = lambda d: _get_bands(inputs, sources, d)
            snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
            snuggs.func_map['sieve'] = lambda *args: sieve(*args)

            # The windows iterator is initialized with a single sample.
            # The actual work windows will be added in the second
            # iteration of the loop.
            work_windows = [(None, Window(0, 0, 16, 16))]

            for ij, window in work_windows:

                ctxkwds = OrderedDict()

                for i, ((name, path), src) in enumerate(zip(inputs, sources)):

                    # Using the class method instead of instance
                    # method. Latter raises
                    #
                    # TypeError: astype() got an unexpected keyword
                    # argument 'copy'
                    #
                    # possibly something to do with the instance being
                    # a masked array.
                    ctxkwds[name or '_i%d' % (i + 1)] = src.read(masked=masked,
                                                                 window=window)

                res = snuggs.eval(command, **ctxkwds)

                if (isinstance(res, np.ma.core.MaskedArray) and
                    (tuple(LooseVersion(np.__version__).version) <
                     (1, 9) or tuple(LooseVersion(np.__version__).version) >
                     (1, 10))):
                    res = res.filled(kwargs['nodata'])

                if len(res.shape) == 3:
                    results = np.ndarray.astype(res, dtype, copy=False)
                else:
                    results = np.asanyarray(
                        [np.ndarray.astype(res, dtype, copy=False)])

                # The first iteration is only to get sample results and from them
                # compute some properties of the output dataset.
                if dst is None:
                    kwargs['count'] = results.shape[0]
                    dst = rasterio.open(output, 'w', **kwargs)
                    work_windows.extend(
                        _chunk_output(dst.width,
                                      dst.height,
                                      dst.count,
                                      np.dtype(dst.dtypes[0]).itemsize,
                                      mem_limit=mem_limit))

                # In subsequent iterations we write results.
                else:
                    dst.write(results, window=window)

    except snuggs.ExpressionError as err:
        click.echo("Expression Error:")
        click.echo('  %s' % err.text)
        click.echo(' ' + ' ' * err.offset + "^")
        click.echo(err)
        raise click.Abort()

    finally:
        if dst:
            dst.close()
        for src in sources:
            src.close()
Пример #59
0
def test_ufunc(truetrue, truefalse):
    result = snuggs.eval(
        '(where (& foo bar) 1 0)', foo=truetrue, bar=truefalse)
    assert list(result.flatten()) == [1.0, 0.0]