Exemple #1
0
def test_compute_consistency(use_testdb, testdb, raster_file_xyz):
    import terracotta
    from terracotta.xyz import get_tile_data
    from terracotta.handlers import compute
    from terracotta.image import to_uint8

    settings = terracotta.get_settings()

    raw_img = compute.compute(
        'v1 + v2',
        ['val21', 'x'],
        {'v1': 'val22', 'v2': 'val23'},
        stretch_range=(0, 10000),
        tile_xyz=raster_file_xyz
    )
    img_data = np.asarray(Image.open(raw_img))
    assert img_data.shape == settings.DEFAULT_TILE_SIZE

    driver = terracotta.get_driver(testdb)

    with driver.connect():
        v1 = get_tile_data(driver, ['val21', 'x', 'val22'], raster_file_xyz)
        v2 = get_tile_data(driver, ['val21', 'x', 'val23'], raster_file_xyz)

    np.testing.assert_array_equal(
        img_data,
        to_uint8(v1 + v2, 0, 10000)
    )
Exemple #2
0
def colormap(*,
             stretch_range: Tuple[Number, Number],
             colormap: str = None,
             num_values: int = 255) -> List[Dict[str, Any]]:
    """Returns a list [{value=pixel value, rgba=rgba tuple}] for given stretch parameters"""
    from terracotta import image

    target_coords = np.linspace(stretch_range[0], stretch_range[1], num_values)

    if colormap is not None:
        from terracotta.cmaps import get_cmap
        cmap = get_cmap(colormap)
    else:
        # assemble greyscale cmap of shape (255, 4)
        cmap = np.ones(shape=(255, 4), dtype='uint8') * 255
        cmap[:, :-1] = np.tile(
            np.arange(1, 256, dtype='uint8')[:, np.newaxis], (1, 3))

    cmap_coords = image.to_uint8(target_coords, *stretch_range) - 1
    colors = cmap[cmap_coords]

    return [
        dict(value=p, rgba=c)
        for p, c in zip(target_coords.tolist(), colors.tolist())
    ]
Exemple #3
0
def test_to_uint8():
    from terracotta import image

    data = np.array([-50, 0, 10, 255, 256, 1000])
    np.testing.assert_array_equal(
        image.to_uint8(data, -50, 1000),
        [1, 13, 16, 75, 75, 255]
    )
Exemple #4
0
def singleband(keys: Union[Sequence[str], Mapping[str, str]],
               tile_xyz: Tuple[int, int, int] = None,
               *,
               colormap: Union[str, Mapping[Number, RGB], None] = None,
               stretch_range: Tuple[Number, Number] = None,
               tile_size: Tuple[int, int] = None) -> BinaryIO:
    """Return singleband image as PNG"""

    cmap_or_palette: Union[str, Sequence[RGB], None]

    if stretch_range is None:
        stretch_min, stretch_max = None, None
    else:
        stretch_min, stretch_max = stretch_range

    preserve_values = isinstance(colormap, collections.Mapping)

    settings = get_settings()
    if tile_size is None:
        tile_size = settings.DEFAULT_TILE_SIZE

    driver = get_driver(settings.DRIVER_PATH,
                        provider=settings.DRIVER_PROVIDER)

    with driver.connect():
        metadata = driver.get_metadata(keys)
        tile_data = xyz.get_tile_data(driver,
                                      keys,
                                      tile_xyz,
                                      tile_size=tile_size,
                                      preserve_values=preserve_values)

    if preserve_values:
        # bin output image into supplied labels, starting at 1
        colormap = cast(Mapping, colormap)

        labels, label_colors = list(colormap.keys()), list(colormap.values())

        cmap_or_palette = label_colors
        out = image.label(tile_data, labels)
    else:
        # determine stretch range from metadata and arguments
        stretch_range_ = list(metadata['range'])

        if stretch_min is not None:
            stretch_range_[0] = stretch_min

        if stretch_max is not None:
            stretch_range_[1] = stretch_max

        cmap_or_palette = cast(Optional[str], colormap)
        out = image.to_uint8(tile_data, *stretch_range_)

    return image.array_to_png(out, colormap=cmap_or_palette)
Exemple #5
0
def rgb(some_keys: Sequence[str],
        rgb_values: Sequence[str],
        tile_xyz: Tuple[int, int, int] = None,
        *,
        stretch_ranges: ListOfRanges = None,
        tile_size: Tuple[int, int] = None) -> BinaryIO:
    """Return RGB image as PNG

    Red, green, and blue channels correspond to the given values `rgb_values` of the key
    missing from `some_keys`.
    """
    import numpy as np

    # make sure all stretch ranges contain two values
    if stretch_ranges is None:
        stretch_ranges = [None, None, None]

    if len(stretch_ranges) != 3:
        raise exceptions.InvalidArgumentsError(
            'stretch_ranges argument must contain 3 values')

    stretch_ranges_ = [
        stretch_range or (None, None) for stretch_range in stretch_ranges
    ]

    if len(rgb_values) != 3:
        raise exceptions.InvalidArgumentsError(
            'rgb_values argument must contain 3 values')

    settings = get_settings()

    if tile_size is None:
        tile_size_ = settings.DEFAULT_TILE_SIZE
    else:
        tile_size_ = tile_size

    driver = get_driver(settings.DRIVER_PATH,
                        provider=settings.DRIVER_PROVIDER)

    with driver.connect():
        key_names = driver.key_names

        if len(some_keys) != len(key_names) - 1:
            raise exceptions.InvalidArgumentsError(
                'must specify all keys except last one')

        def get_band_future(band_key: str) -> Future:
            band_keys = (*some_keys, band_key)
            return xyz.get_tile_data(driver,
                                     band_keys,
                                     tile_xyz=tile_xyz,
                                     tile_size=tile_size_,
                                     asynchronous=True)

        futures = [get_band_future(key) for key in rgb_values]
        band_items = zip(rgb_values, stretch_ranges_, futures)

        out_arrays = []

        for i, (band_key, band_stretch_override,
                band_data_future) in enumerate(band_items):
            keys = (*some_keys, band_key)
            metadata = driver.get_metadata(keys)

            band_stretch_range = list(metadata['range'])
            scale_min, scale_max = band_stretch_override

            if scale_min is not None:
                band_stretch_range[0] = scale_min

            if scale_max is not None:
                band_stretch_range[1] = scale_max

            if band_stretch_range[1] < band_stretch_range[0]:
                raise exceptions.InvalidArgumentsError(
                    'Upper stretch bound must be higher than lower bound')

            band_data = band_data_future.result()
            out_arrays.append(image.to_uint8(band_data, *band_stretch_range))

    out = np.ma.stack(out_arrays, axis=-1)
    return image.array_to_png(out)
Exemple #6
0
def compute(expression: str,
            some_keys: Sequence[str],
            operand_keys: Mapping[str, str],
            stretch_range: Tuple[Number, Number],
            tile_xyz: Tuple[int, int, int] = None,
            *,
            colormap: str = None,
            tile_size: Tuple[int, int] = None) -> BinaryIO:
    """Return singleband image computed from one or more images as PNG

    Expects a Python expression that returns a NumPy array. Operands in
    the expression are replaced by the images with keys as defined by
    some_keys (all but the last key) and operand_keys (last key).

    Contrary to singleband and rgb handlers, stretch_range must be given.

    Example:

        >>> operands = {
        ...     'v1': 'B08',
        ...     'v2': 'B04'
        ... }
        >>> compute('v1 * v2', ['S2', '20171101'], operands, [0, 1000])
        <binary image containing product of bands 4 and 8>

    """
    from terracotta.expressions import evaluate_expression

    if not stretch_range[1] > stretch_range[0]:
        raise exceptions.InvalidArgumentsError(
            'Upper stretch bounds must be larger than lower bounds')

    settings = get_settings()

    if tile_size is None:
        tile_size_ = settings.DEFAULT_TILE_SIZE
    else:
        tile_size_ = tile_size

    driver = get_driver(settings.DRIVER_PATH,
                        provider=settings.DRIVER_PROVIDER)

    with driver.connect():
        key_names = driver.key_names

        if len(some_keys) != len(key_names) - 1:
            raise exceptions.InvalidArgumentsError(
                'must specify all keys except last one')

        def get_band_future(band_key: str) -> Future:
            band_keys = (*some_keys, band_key)
            return xyz.get_tile_data(driver,
                                     band_keys,
                                     tile_xyz=tile_xyz,
                                     tile_size=tile_size_,
                                     asynchronous=True)

        futures = {
            var: get_band_future(key)
            for var, key in operand_keys.items()
        }
        operand_data = {
            var: future.result()
            for var, future in futures.items()
        }

    try:
        out = evaluate_expression(expression, operand_data)
    except ValueError as exc:
        # make sure error message gets propagated
        raise exceptions.InvalidArgumentsError(
            f'error while executing expression: {exc!s}')

    out = image.to_uint8(out, *stretch_range)
    return image.array_to_png(out, colormap=colormap)