def test_array_to_png_singleband(): from terracotta import image testdata = np.random.randint(0, 256, size=(256, 512), dtype='uint8') out_img = Image.open(image.array_to_png(testdata)).convert('RGBA') out_data = np.asarray(out_img) assert out_data.shape[:-1] == testdata.shape assert np.all(out_data[testdata == 0, -1] == 0) assert np.all(out_data[testdata != 0, -1] == 255) np.testing.assert_array_equal(testdata, out_data[..., 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)
def test_array_to_png_singleband_invalid(): from terracotta import image, exceptions with pytest.raises(exceptions.InvalidArgumentsError) as exc: image.array_to_png(np.zeros((20, 20)), colormap='unknown') assert 'invalid color map' in str(exc.value) with pytest.raises(exceptions.InvalidArgumentsError) as exc: image.array_to_png(np.zeros((20, 20)), colormap=[(0, 0, 0, 0)] * 1000) assert 'must contain less' in str(exc.value) with pytest.raises(ValueError) as exc: image.array_to_png(np.zeros((20, 20)), colormap=[(0, 0, 0)] * 10) assert 'must have shape' in str(exc.value)
def test_array_to_png_rgb_invalid(): from terracotta import image too_many_bands = np.random.randint(0, 256, size=(256, 512, 4), dtype='uint8') with pytest.raises(ValueError) as exc: image.array_to_png(too_many_bands) assert 'must have three bands' in str(exc.value) with pytest.raises(ValueError) as exc: image.array_to_png(np.zeros((20, 20, 3)), colormap='viridis') assert 'Colormap argument cannot be given' in str(exc.value) with pytest.raises(ValueError) as exc: image.array_to_png(np.array([])) assert '2 or 3 dimensions' in str(exc.value)
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)
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)