def test_expression_missing(): """Should raise an exception on missing expression.""" tile_z = 19 tile_x = 109554 tile_y = 200458 prefix = os.path.join(os.path.dirname(__file__), "fixtures") sceneid = "{}/my-bucket/hro_sources/colorado/201404_13SED190110_201404_0x1500m_CL_1.tif".format( prefix) with pytest.raises(Exception): utils.expression(sceneid, tile_x, tile_y, tile_z, tilesize=512)
def tile( scene: str, z: int, x: int, y: int, scale: int = 1, ext: str = "png", bands: str = None, percents: str = "", expr: str = None, rescale: str = None, color_formula: str = None, color_map: str = None, ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" driver = "jpeg" if ext == "jpg" else ext if bands and expr: raise CbersTilerError("Cannot pass bands and expression") tilesize = scale * 256 if expr is not None: tile, mask = expression(scene, x, y, z, expr=expr, tilesize=tilesize) elif bands is not None: tile, mask = cbers_tile(scene, x, y, z, bands=tuple(bands.split(",")), tilesize=tilesize, percents=percents) else: raise CbersTilerError("No bands nor expression given") if tile is None or mask is None: return ( "OK", f"image/png", b'', ) rtile, rmask = _postprocess(tile, mask, rescale=None, color_formula=color_formula) if color_map: color_map = get_colormap(color_map, format="gdal") options = img_profiles.get(driver, {}) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )
def ratio(scene, tile_z, tile_x, tile_y, tileformat): """Handle processing requests """ if tileformat == 'jpg': tileformat = 'jpeg' query_args = APP.current_request.query_params query_args = query_args if isinstance(query_args, dict) else {} ratio_value = query_args['ratio'] range_value = query_args.get('range', [-1, 1]) tilesize = query_args.get('tile', 256) tilesize = int(tilesize) if isinstance(tilesize, str) else tilesize tile, mask = expression(scene, tile_x, tile_y, tile_z, ratio_value, tilesize=tilesize) if len(tile.shape) == 2: tile = np.expand_dims(tile, axis=0) rtile = np.where( mask, linear_rescale(tile, in_range=range_value, out_range=[0, 255]), 0).astype(np.uint8) img = array_to_img(rtile, color_map=get_colormap(name='cfastie'), mask=mask) str_img = b64_encode_img(img, tileformat) return ('OK', f'image/{tileformat}', str_img)
def test_expression_main_ratio(): """Should work as expected.""" expr = "(b3 - b2) / (b3 + b2)" tile_z = 19 tile_x = 109554 tile_y = 200458 prefix = os.path.join(os.path.dirname(__file__), "fixtures") sceneid = "{}/my-bucket/hro_sources/colorado/201404_13SED190110_201404_0x1500m_CL_1.tif".format( prefix) data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (1, 256, 256) mask.shape == (256, 256) data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr=expr) data.shape == (1, 256, 256) mask.shape == (256, 256)
def tile( scene, z, x, y, scale=1, ext="png", bands=None, expr=None, rescale=None, color_formula=None, color_map=None, ): """Handle tile requests.""" if ext == "jpg": driver = "jpeg" elif ext == "jp2": driver = "JP2OpenJPEG" else: driver = ext if bands and expr: raise CbersTilerError("Cannot pass bands and expression") if not bands and not expr: raise CbersTilerError("Need bands or expression") if bands: bands = tuple(bands.split(",")) tilesize = scale * 256 if expr is not None: tile, mask = expression(scene, x, y, z, expr, tilesize=tilesize) elif bands is not None: tile, mask = cbers.tile(scene, x, y, z, bands=bands, tilesize=tilesize) rtile, rmask = _postprocess(tile, mask, tilesize, rescale=rescale, color_formula=color_formula) if color_map: color_map = get_colormap(color_map, format="gdal") options = img_profiles.get(driver, {}) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )
def tile( z: int, x: int, y: int, scale: int = 1, ext: str = None, url: str = None, indexes: Union[str, Tuple[int]] = None, expr: str = None, nodata: Union[str, int, float] = None, rescale: str = None, color_formula: str = None, color_map: str = None, ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" driver = "jpeg" if ext == "jpg" else ext if indexes and expr: raise TilerError("Cannot pass indexes and expression") if not url: raise TilerError("Missing 'url' parameter") if isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) tilesize = scale * 256 if expr is not None: tile, mask = expression( url, x, y, z, expr=expr, tilesize=tilesize, nodata=nodata ) else: tile, mask = main.tile( url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata ) rtile, rmask = _postprocess( tile, mask, rescale=rescale, color_formula=color_formula ) if color_map: color_map = get_colormap(color_map, format="gdal") options = img_profiles.get(driver, {}) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )
def test_expression_main_kwargs(): """ Should work as expected """ expr = '(b4 - b3) / (b4 + b3)' tile_z = 19 tile_x = 109554 tile_y = 200458 prefix = os.path.join(os.path.dirname(__file__), 'fixtures') sceneid = '{}/my-bucket/hro_sources/colorado/201404_13SED190110_201404_0x1500m_CL_1_alpha.tif'.format(prefix) data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr, tilesize=512) data.shape == (1, 512, 512) mask.shape == (512, 512)
def test_expression_main_rgb(): """ Should work as expected """ expr = 'b1*0.8, b2*1.1, b3*0.8' tile_z = 19 tile_x = 109554 tile_y = 200458 prefix = os.path.join(os.path.dirname(__file__), 'fixtures') sceneid = '{}/my-bucket/hro_sources/colorado/201404_13SED190110_201404_0x1500m_CL_1.tif'.format(prefix) data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (3, 256, 256) mask.shape == (256, 256)
def test_expression_cbers_rgb(cbers_tile): """Should read tile from CBERS data.""" cbers_tile.return_value = [ np.random.randint(0, 255, size=(3, 256, 256), dtype=np.uint8), np.random.randint(0, 1, size=(256, 256), dtype=np.uint8) * 255, ] expr = "b8*0.8, b7*1.1, b6*0.8" tile_z = 10 tile_x = 664 tile_y = 495 sceneid = "CBERS_4_MUX_20171121_057_094_L2" data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (3, 512, 512) mask.shape == (512, 512) assert len(cbers_tile.call_args[1].get("bands")) == 3
def test_expression_landsat_rgb(landsat_tile): """Should work as expected.""" landsat_tile.return_value = [ np.random.randint(0, 255, size=(3, 256, 256), dtype=np.uint8), np.random.randint(0, 1, size=(256, 256), dtype=np.uint8) * 255, ] expr = "b5*0.8, b4*1.1, b3*0.8" tile_z = 8 tile_x = 71 tile_y = 102 sceneid = "LC08_L1TP_016037_20170813_20170814_01_RT" data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (3, 512, 512) mask.shape == (512, 512) assert len(landsat_tile.call_args[1].get("bands")) == 3
def test_expression_ndvi(landsat_tile): """Should work as expected""" landsat_tile.return_value = [ np.random.randint(0, 255, size=(2, 256, 256), dtype=np.uint8), np.random.randint(0, 1, size=(256, 256), dtype=np.uint8) * 255, ] expr = "(b5 - b4) / (b5 + b4)" tile_z = 8 tile_x = 71 tile_y = 102 sceneid = "LC08_L1TP_016037_20170813_20170814_01_RT" data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (1, 256, 256) mask.shape == (256, 256) assert len(landsat_tile.call_args[1].get("bands")) == 2
def test_expression_sentinel2(sentinel2): """Should work as expected.""" sentinel2.return_value = [ np.random.randint(0, 255, size=(2, 256, 256), dtype=np.uint8), np.random.randint(0, 1, size=(256, 256), dtype=np.uint8) * 255, ] expr = "(b8A - b12) / (b8A + b12)" tile_z = 8 tile_x = 71 tile_y = 102 sceneid = "S2A_tile_20170323_17SNC_0" data, mask = utils.expression(sceneid, tile_x, tile_y, tile_z, expr) data.shape == (1, 256, 256) mask.shape == (256, 256) assert sorted(list(sentinel2.call_args[1].get("bands"))) == ["12", "8A"]
def tile_handler( z: int, x: int, y: int, scale: int = 1, ext: str = None, url: str = None, indexes: Union[str, Tuple[int]] = None, expr: str = None, nodata: Union[str, int, float] = None, rescale: str = None, color_formula: str = None, color_map: str = None, ) -> Tuple[str, str, BinaryIO]: """Handle /tiles requests.""" if indexes and expr: raise TilerError("Cannot pass indexes and expression") if not url: raise TilerError("Missing 'url' parameter") if isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) tilesize = scale * 256 if expr is not None: tile, mask = expression( url, x, y, z, expr=expr, tilesize=tilesize, nodata=nodata ) else: tile, mask = cogTiler.tile( url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata ) if not ext: ext = "jpg" if mask.all() else "png" rtile, rmask = _postprocess( tile, mask, rescale=rescale, color_formula=color_formula ) if color_map: color_map = get_colormap(color_map, format="gdal") if ext == "jpg": driver = "jpeg" elif ext == "tif": driver = "GTiff" else: driver = ext options = img_profiles.get(driver, {}) if driver == "GTiff": mercator_tile = mercantile.Tile(x=x, y=y, z=z) bounds = mercantile.xy_bounds(mercator_tile) w, s, e, n = bounds dst_transform = from_bounds(w, s, e, n, rtile.shape[1], rtile.shape[2]) options = dict( dtype=rtile.dtype, crs={"init": "EPSG:3857"}, transform=dst_transform ) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )
def get(self, request, pk=None, project_pk=None, tile_type="", z="", x="", y="", scale=1): """ Get a tile image """ task = self.get_and_check_task(request, pk) z = int(z) x = int(x) y = int(y) scale = int(scale) ext = "png" driver = "jpeg" if ext == "jpg" else ext indexes = None nodata = None formula = self.request.query_params.get('formula') bands = self.request.query_params.get('bands') rescale = self.request.query_params.get('rescale') color_map = self.request.query_params.get('color_map') hillshade = self.request.query_params.get('hillshade') if formula == '': formula = None if bands == '': bands = None if rescale == '': rescale = None if color_map == '': color_map = None if hillshade == '' or hillshade == '0': hillshade = None try: expr, _ = lookup_formula(formula, bands) except ValueError as e: raise exceptions.ValidationError(str(e)) if tile_type in ['dsm', 'dtm'] and rescale is None: rescale = "0,1000" if tile_type in ['dsm', 'dtm'] and color_map is None: color_map = "gray" if tile_type == 'orthophoto' and formula is not None: if color_map is None: color_map = "gray" if rescale is None: rescale = "-1,1" if nodata is not None: nodata = np.nan if nodata == "nan" else float(nodata) tilesize = scale * 256 url = get_raster_path(task, tile_type) if not os.path.isfile(url): raise exceptions.NotFound() with rasterio.open(url) as src: minzoom, maxzoom = get_zoom_safe(src) has_alpha = has_alpha_band(src) if z < minzoom - ZOOM_EXTRA_LEVELS or z > maxzoom + ZOOM_EXTRA_LEVELS: raise exceptions.NotFound() # Handle N-bands datasets for orthophotos (not plant health) if tile_type == 'orthophoto' and expr is None: ci = src.colorinterp # More than 4 bands? if len(ci) > 4: # Try to find RGBA band order if ColorInterp.red in ci and \ ColorInterp.green in ci and \ ColorInterp.blue in ci: indexes = ( ci.index(ColorInterp.red) + 1, ci.index(ColorInterp.green) + 1, ci.index(ColorInterp.blue) + 1, ) else: # Fallback to first three indexes = ( 1, 2, 3, ) elif has_alpha: indexes = non_alpha_indexes(src) resampling = "nearest" padding = 0 if tile_type in ["dsm", "dtm"]: resampling = "bilinear" padding = 16 try: if expr is not None: tile, mask = expression(url, x, y, z, expr=expr, tilesize=tilesize, nodata=nodata, tile_edge_padding=padding, resampling_method=resampling) else: tile, mask = main.tile(url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata, tile_edge_padding=padding, resampling_method=resampling) except TileOutsideBounds: raise exceptions.NotFound("Outside of bounds") if color_map: try: color_map = get_colormap(color_map, format="gdal") except FileNotFoundError: raise exceptions.ValidationError("Not a valid color_map value") intensity = None if hillshade is not None: try: hillshade = float(hillshade) if hillshade <= 0: hillshade = 1.0 except ValueError: raise exceptions.ValidationError("Invalid hillshade value") if tile.shape[0] != 1: raise exceptions.ValidationError( "Cannot compute hillshade of non-elevation raster (multiple bands found)" ) delta_scale = (maxzoom + ZOOM_EXTRA_LEVELS + 1 - z) * 4 dx = src.meta["transform"][0] * delta_scale dy = -src.meta["transform"][4] * delta_scale ls = LightSource(azdeg=315, altdeg=45) # Hillshading is not a local tile operation and # requires neighbor tiles to be rendered seamlessly elevation = get_elevation_tiles(tile[0], url, x, y, z, tilesize, nodata, resampling, padding) intensity = ls.hillshade(elevation, dx=dx, dy=dy, vert_exag=hillshade) intensity = intensity[tilesize:tilesize * 2, tilesize:tilesize * 2] rgb, rmask = rescale_tile(tile, mask, rescale=rescale) rgb = apply_colormap(rgb, color_map) if intensity is not None: # Quick check if rgb.shape[0] != 3: raise exceptions.ValidationError( "Cannot process tile: intensity image provided, but no RGB data was computed." ) intensity = intensity * 255.0 rgb = hsv_blend(rgb, intensity) options = img_profiles.get(driver, {}) return HttpResponse(array_to_image(rgb, rmask, img_format=driver, **options), content_type="image/{}".format(ext))
def tile( z, x, y, scale=1, ext="png", url=None, indexes=None, expr=None, nodata=None, rescale=None, color_formula=None, color_map=None, dem=None, ): """Handle tile requests.""" if ext == "jpg": driver = "jpeg" elif ext == "jp2": driver = "JP2OpenJPEG" else: driver = ext if indexes and expr: raise TilerError("Cannot pass indexes and expression") if not url: raise TilerError("Missing 'url' parameter") if indexes: indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) tilesize = scale * 256 if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) if expr is not None: tile, mask = expression(url, x, y, z, expr, tilesize=tilesize) else: tile, mask = main.tile(url, x, y, z, indexes=indexes, tilesize=tilesize) if dem: if dem == "mapbox": tile = encoders.data_to_rgb(tile, -10000, 1) elif dem == "mapzen": tile = mapzen_elevation_rgb.data_to_rgb(tile) else: return ("NOK", "text/plain", 'Invalid "dem" mode') else: rtile, rmask = _postprocess(tile, mask, tilesize, rescale=rescale, color_formula=color_formula) if color_map: color_map = get_colormap(color_map, format="gdal") options = img_profiles.get(driver, {}) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )
def get(self, request, pk=None, project_pk=None, tile_type="", z="", x="", y="", scale=1): """ Get a tile image """ task = self.get_and_check_task(request, pk) z = int(z) x = int(x) y = int(y) if x == 0 and y == 0 and z == 0: raise exceptions.NotFound() scale = int(scale) ext = "png" driver = "jpeg" if ext == "jpg" else ext indexes = None nodata = None formula = self.request.query_params.get('formula') bands = self.request.query_params.get('bands') rescale = self.request.query_params.get('rescale') color_map = self.request.query_params.get('color_map') hillshade = self.request.query_params.get('hillshade') if formula == '': formula = None if bands == '': bands = None if rescale == '': rescale = None if color_map == '': color_map = None if hillshade == '' or hillshade == '0': hillshade = None try: expr, _ = lookup_formula(formula, bands) except ValueError as e: raise exceptions.ValidationError(str(e)) if tile_type in ['dsm', 'dtm'] and rescale is None: rescale = "0,1000" if tile_type in ['dsm', 'dtm'] and color_map is None: color_map = "gray" if tile_type == 'orthophoto' and formula is not None: if color_map is None: color_map = "gray" if rescale is None: rescale = "-1,1" if nodata is not None: nodata = np.nan if nodata == "nan" else float(nodata) tilesize = scale * 256 url = get_raster_path(task, tile_type) if not os.path.isfile(url): raise exceptions.NotFound() try: if expr is not None: tile, mask = expression(url, x, y, z, expr=expr, tilesize=tilesize, nodata=nodata) else: tile, mask = main.tile(url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata) except TileOutsideBounds: raise exceptions.NotFound("Outside of bounds") # Use alpha channel for transparency, don't use the mask if one is provided (redundant) if tile.shape[0] == 4: mask = None if color_map: try: color_map = get_colormap(color_map, format="gdal") except FileNotFoundError: raise exceptions.ValidationError("Not a valid color_map value") intensity = None if hillshade is not None: try: hillshade = float(hillshade) if hillshade <= 0: hillshade = 1.0 print(hillshade) except ValueError: raise exceptions.ValidationError("Invalid hillshade value") if tile.shape[0] != 1: raise exceptions.ValidationError( "Cannot compute hillshade of non-elevation raster (multiple bands found)" ) with rasterio.open(url) as src: minzoom, maxzoom = get_zooms(src) z_value = min(maxzoom, max(z, minzoom)) delta_scale = (maxzoom + 1 - z_value) * 4 dx = src.meta["transform"][0] * delta_scale dy = -src.meta["transform"][4] * delta_scale ls = LightSource(azdeg=315, altdeg=45) # Hillshading is not a local tile operation and # requires neighbor tiles to be rendered seamlessly elevation = get_elevation_tiles(tile[0], url, x, y, z, tilesize, nodata) intensity = ls.hillshade(elevation, dx=dx, dy=dy, vert_exag=hillshade) intensity = intensity[tilesize:tilesize * 2, tilesize:tilesize * 2] rgb, rmask = rescale_tile(tile, mask, rescale=rescale) rgb = apply_colormap(rgb, color_map) if intensity is not None: # Quick check if rgb.shape[0] != 3: raise exceptions.ValidationError( "Cannot process tile: intensity image provided, but no RGB data was computed." ) intensity = intensity * 255.0 rgb = hsv_blend(rgb, intensity) options = img_profiles.get(driver, {}) return HttpResponse(array_to_image(rgb, rmask, img_format=driver, **options), content_type="image/{}".format(ext))