def test_imageprofile(): """test image profile.""" ImageType.png.profile == img_profiles.get("png") ImageType.pngraw.profile == img_profiles.get("pngraw") ImageType.jpg.profile == img_profiles.get("jpg") ImageType.jpeg.profile == img_profiles.get("jpeg") ImageType.webp.profile == img_profiles.get("webp")
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 reformat( data: numpy.ndarray, mask: numpy.ndarray, img_format: ImageType, colormap: Optional[Dict[int, Tuple[int, int, int, int]]] = None, transform: Optional[affine.Affine] = None, crs: Optional[CRS] = None, ): """Reformat image data to bytes""" if img_format == ImageType.npy: sio = BytesIO() numpy.save(sio, (data, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[img_format.value] options = img_profiles.get(driver.lower(), {}) if transform and crs and ImageType.tif in img_format: options = {"crs": crs, "transform": transform} content = render(data, mask, img_format=driver, colormap=colormap, **options) return content
def _img( mosaicid: str = None, z: int = None, x: int = None, y: int = None, scale: int = 1, ext: str = None, url: str = None, pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple: """Handle tile requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.tile(x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") tilesize = 256 * scale if pixel_selection == "last": pixel_selection = "first" assets = list(reversed(assets)) with rasterio.Env(aws_session): pixsel_method = PIXSEL_METHODS[pixel_selection] tile, mask = mosaic_tiler( assets, x, y, z, usgs_tiler, tilesize=tilesize, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") if not ext: ext = "jpg" if mask.all() else "png" driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" options = geotiff_options(x, y, z, tilesize) return ( "OK", f"image/{ext}", render(tile, mask, img_format=driver, **options), )
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_gdal_profiles(): """Return GDAL compatible profiles.""" assert img_profiles["jpeg"] assert img_profiles["png"] assert img_profiles["pngraw"] assert img_profiles["webp"] with pytest.raises(KeyError): img_profiles["wepc"] prof = img_profiles.get("jpeg") prof["test"] = True new_prof = img_profiles.get("jpeg") assert not new_prof.get("test") prof = img_profiles["jpeg"] prof["test"] = True new_prof = img_profiles["jpeg"] assert not new_prof.get("test") prof = img_profiles.get("jpe", {"a": "b"}) assert prof == {"a": "b"}
async def stac_tile( z: int, x: int, y: int, scale: int = Query(2, gt=0, lt=4), ext: ImageType = None, assets: Any = Query( None, description="Coma (',') delimited band indexes"), indexes: Any = Query( None, description="Coma (',') delimited band indexes"), rescale: Any = Query( None, description="Coma (',') delimited Min,Max bounds"), color_formula: str = Query(None, description="rio-color formula"), color_map: str = Query(None, description="rio-tiler color map names"), resampling_method: str = Query("bilinear", description="rasterio resampling"), ): """Handle /tiles requests.""" if isinstance(assets, str): assets = assets.split(",") if isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) tilesize = scale * 256 tile, mask = self.raster.read_tile( z, x, y, assets, tilesize=tilesize, indexes=indexes, resampling_method=resampling_method, ) tile = await _postprocess_tile(tile, mask, rescale=rescale, color_formula=color_formula) if not ext: ext = ImageType.jpg if mask.all() else ImageType.png driver = drivers[ext] options = img_profiles.get(driver.lower(), {}) if color_map: options["colormap"] = cmap.get(color_map) content = await _render(tile, mask, img_format=driver, **options) return TileResponse(content, media_type=mimetype[ext.value])
def reformat( data: numpy.ndarray, mask: numpy.ndarray, img_format: ImageType, colormap: Optional[Dict[int, Tuple[int, int, int, int]]] = None, transform: Optional[affine.Affine] = None, crs: Optional[CRS] = None, ): """Reformat image data to bytes""" driver = drivers[img_format.value] options = img_profiles.get(driver.lower(), {}) if transform and crs and ImageType.tif in img_format: options = {"crs": crs, "transform": transform} return render(data, mask, img_format=driver, colormap=colormap, **options)
def _img( z: int = None, x: int = None, y: int = None, tile_size: Union[str, int] = 256, ext: str = 'png', url: str = None, encoding: str = 'terrarium', pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple: """Handle tile requests.""" if not url: return ("NOK", "text/plain", "Missing URL parameter") tile_size = int(tile_size) assets = find_assets(x, y, z, url, tile_size) if assets is None: return ("NOK", "text/plain", "no assets found") rgb = load_assets(x, y, z, assets, tile_size, input_format=url, output_format=encoding, pixel_selection=pixel_selection, resampling_method=resampling_method) if rgb is None: return ("EMPTY", "text/plain", "empty tiles") driver = ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" options = geotiff_options(x, y, z, tile_size) return ( "OK", f"image/{ext}", render(rgb, img_format=driver, **options), )
def s1tile( scene: str, z: int, x: int, y: int, scale: int = 1, ext: str = "png", bands: str = None, rescale: str = None, color_formula: str = None, color_map: str = None, ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" if not bands: raise Exception("bands is required") tilesize = scale * 256 tile, mask = sentinel1.tile(scene, x, y, z, bands=tuple(bands.split(",")), tilesize=tilesize) rtile, rmask = _postprocess(tile, mask, rescale=rescale, color_formula=color_formula) if color_map: color_map = get_colormap(color_map, format="gdal") driver = "jpeg" if ext == "jpg" else ext 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_tile(self, z, x, y, tileformat, color_ops=None): if tileformat == "jpg": tileformat = "jpeg" if not self.raster.tile_exists(z, x, y): raise web.HTTPError(404) data, mask = self.raster.read_tile(z, x, y) if len(data.shape) == 2: data = numpy.expand_dims(data, axis=0) if self.scale: nbands = data.shape[0] scale = self.scale if len(scale) != nbands: scale = scale * nbands for bdx in range(nbands): data[bdx] = numpy.where( mask, linear_rescale(data[bdx], in_range=scale[bdx], out_range=[0, 255]), 0, ) data = data.astype(numpy.uint8) if color_ops: data = self._apply_color_operations(data, color_ops) options = img_profiles.get(tileformat, {}) return BytesIO( array_to_image(data, mask=mask, color_map=self.colormap, img_format=tileformat, **options))
async def tile( z: int, x: int, y: int, ext: str = Path(..., regex="^(png)|(jpg)|(webp)$"), scale: int = 1, indexes: str = Query(None, title="Coma (',') delimited band indexes"), rescale: str = Query(None, title="Coma (',') delimited Min,Max bounds"), color_formula: str = Query(None, title="rio-color formula"), color_map: str = Query(None, title="rio-tiler color map names"), resampling_method: str = Query("bilinear", title="rasterio resampling"), ): """Handle /tiles requests.""" if isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) tilesize = scale * 256 tile, mask = self.raster.read_tile( z, x, y, tilesize=tilesize, indexes=indexes, resampling_method=resampling_method, ) rtile, _ = postprocess_tile( tile, mask, rescale=rescale, color_formula=color_formula ) if color_map: color_map = get_colormap(color_map, format="gdal") driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) img = array_to_image( rtile, mask, img_format=driver, color_map=color_map, **options ) return TileResponse(img, media_type=f"image/{ext}")
def _tile( z: int, x: int, y: int, scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."), identifier: str = Query("WebMercatorQuad", title="TMS identifier"), filename: str = Query(...), ): """Handle /tiles requests.""" tms = morecantile.tms.get(identifier) with COGReader(f"{filename}.tif", tms=tms) as cog: # type: ignore tile, mask = cog.tile(x, y, z, tilesize=scale * 256) ext = ImageType.png driver = drivers[ext.value] options = img_profiles.get(driver.lower(), {}) img = render(tile, mask, img_format="png", **options) return TileResponse(img, media_type=mimetype[ext.value])
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 tile_handler( z: int, x: int, y: int, scale: int = 1, ext: str = None, url: str = None, indexes: Optional[Tuple[int]] = None, expression: str = None, nodata: Union[str, int, float] = None, rescale: str = None, color_formula: str = None, color_map: str = None, alpha_classes: Tuple[int] = None, # Experiment ignore_classes: Tuple[int] = None, # Experiment resampling_method: str = "nearest", ) -> Tuple[str, str, bytes]: """Handle /tiles requests.""" if indexes and expression: 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 isinstance(alpha_classes, str): alpha_classes = tuple(int(s) for s in alpha_classes.split(",")) if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) if scale is not None and isinstance(scale, str): scale = int(scale) tilesize: int = scale * 256 tile_params: Dict = dict( tilesize=tilesize, nodata=nodata, resampling_method=resampling_method ) colormap = cmap.get_colormap(color_map) if color_map else None with rasterio.Env(aws_session): with CogeoReader(url) as cog: if expression is not None: tile, mask = cog.expression(x, y, z, expression, **tile_params) else: tile, mask = cog.tile(x, y, z, indexes=indexes, **tile_params) if cog.colormap and not colormap: colormap = cog.colormap if alpha_classes: cmap._update_alpha(colormap, alpha_classes) elif ignore_classes: cmap._remove_value(colormap, ignore_classes) if not ext: ext = "jpg" if mask.all() else "png" tile = utils.postprocess(tile, mask, rescale=rescale, color_formula=color_formula) if ext == "npy": sio = io.BytesIO() numpy.save(sio, (tile, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[ext] options = ( img_profiles.get(driver.lower(), {}) if not ext == "tif" else utils.geotiff_options(x, y, z, tilesize) ) content = utils.render( tile, mask, img_format=driver, colormap=colormap, **options ) return ("OK", mimetype[ext], content)
def tiles( mosaicid: str, z: int, x: int, y: int, scale: int = 1, ext: str = "png", bands: str = None, expr: str = None, rescale: str = None, color_ops: str = None, color_map: str = None, pixel_selection: str = "first", ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" bucket = os.environ["MOSAIC_DEF_BUCKET"] url = f"s3://{bucket}/mosaics/{mosaicid}.json.gz" assets = get_assets(url, x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") tilesize = 256 * scale pixel_selection = pixSel[pixel_selection] if expr is not None: tile, mask = mosaic_tiler( assets, x, y, z, expressionTiler, pixel_selection=pixel_selection(), expr=expr, tilesize=tilesize, ) elif bands is not None: tile, mask = mosaic_tiler( assets, x, y, z, landsatTiler, pixel_selection=pixel_selection(), bands=tuple(bands.split(",")), tilesize=tilesize, ) else: return ("NOK", "text/plain", "No bands nor expression given") if tile is None: return ("EMPTY", "text/plain", "empty tiles") if color_map: color_map = get_colormap(color_map, format="gdal") if ext == "gif": frames = [] options = img_profiles.get("png", {}) for i in range(len(tile)): img = post_process_tile(tile[i].copy(), mask[i].copy(), rescale=rescale, color_formula=color_ops) frames.append( Image.open( io.BytesIO( array_to_image( img, mask[i], img_format="png", color_map=color_map, **options, )))) sio = io.BytesIO() frames[0].save( sio, "gif", save_all=True, append_images=frames[1:], duration=300, loop=0, optimize=True, ) sio.seek(0) return ("OK", f"image/{ext}", sio.getvalue()) rtile = post_process_tile(tile, mask, rescale=rescale, color_formula=color_ops) driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) options = dict( crs={"init": "EPSG:3857"}, transform=from_bounds(*tile_bounds, tilesize, tilesize), ) return ( "OK", f"image/{ext}", array_to_image(rtile, mask, img_format=driver, color_map=color_map, **options), )
async def tile( z: int = Path(..., ge=0, le=30, description="Mercator tiles's zoom level"), x: int = Path(..., description="Mercator tiles's column"), y: int = Path(..., description="Mercator tiles's row"), scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."), ext: ImageType = Query( None, description="Output image type. Default is auto."), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), bidx: Optional[str] = Query( None, description="Coma (',') delimited band indexes"), nodata: Optional[Union[str, int, float]] = Query( None, description="Overwrite internal Nodata value."), rescale: Optional[str] = Query( None, description="Coma (',') delimited Min,Max bounds"), color_formula: Optional[str] = Query(None, title="rio-color formula"), color_map: Optional[utils.ColorMapName] = Query( None, title="rio-tiler color map name"), cache_client: CacheLayer = Depends(utils.get_cache), ) -> TileResponse: """Handle /tiles requests.""" timings = [] headers: Dict[str, str] = {} tile_hash = utils.get_hash(**dict( z=z, x=x, y=y, ext=ext, scale=scale, url=url, bidx=bidx, nodata=nodata, rescale=rescale, color_formula=color_formula, color_map=color_map.value if color_map else "", )) tilesize = scale * 256 content = None if cache_client: try: content, ext = cache_client.get_image_from_cache(tile_hash) headers["X-Cache"] = "HIT" except Exception: content = None if not content: indexes = tuple(int(s) for s in re.findall(r"\d+", bidx)) if bidx else None if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) with utils.Timer() as t: tile, mask = await _tile(url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata) timings.append(("Read", t.elapsed)) if not ext: ext = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: tile = await _postprocess(tile, mask, rescale=rescale, color_formula=color_formula) timings.append(("Post-process", t.elapsed)) if color_map: if color_map.value.startswith("custom_"): color_map = utils.get_custom_cmap( color_map.value) # type: ignore else: color_map = get_colormap(color_map.value) # type: ignore with utils.Timer() as t: if ext == ImageType.npy: sio = BytesIO() numpy.save(sio, (tile, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[ext.value] options = img_profiles.get(driver.lower(), {}) if ext == ImageType.tif: options = geotiff_options(x, y, z, tilesize=tilesize) content = await _render(tile, mask, img_format=driver, colormap=color_map, **options) timings.append(("Format", t.elapsed)) if cache_client and content: cache_client.set_image_cache(tile_hash, (content, ext)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return TileResponse(content, media_type=mimetype[ext.value], headers=headers)
def mosaic_tiles( z, x, y, scale=1, ext="png", urls=None, nodata=None, indexes=None, rescale=None, color_ops=None, color_map=None, pixel_selection: str = "first", resampling_method: str = "bilinear", ): """ Handle Raster /mosaics requests. Note: All the querystring parameters are translated to function keywords and passed as string value by lambda_proxy Attributes ---------- z : int, required Mercator tile ZOOM level. x : int, required Mercator tile X index. y : int, required Mercator tile Y index. scale : int Output scale factor (default: 1). ext : str Image format to return (default: png). urls : str, required Dataset urls to read from. indexes : str, optional, (defaults: None) Comma separated band index number (e.g "1,2,3"). nodata, str, optional Custom nodata value if not preset in dataset. rescale : str, optional Min and Max data bounds to rescale data from. color_ops : str, optional rio-color compatible color formula color_map : str, optional Rio-tiler compatible colormap name ("cfastie" or "schwarzwald") pixel_selection : str, optional rio-tiler-mosaic pixel selection method (default: first) resampling_method : str, optional Resampling method to use (default: bilinear) Returns ------- status : str Status of the request (e.g. OK, NOK). MIME type : str response body MIME type (e.g. image/jpeg). body : bytes Image body. """ if not urls: raise TilerError("Missing 'urls' parameter") if indexes: indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) if nodata is not None and isinstance(nodata, str): nodata = numpy.nan if nodata == "nan" else float(nodata) tilesize = 256 * scale tile, mask = mosaic_tiler( urls.split(","), x, y, z, main.tile, indexes=indexes, tilesize=tilesize, nodata=nodata, pixel_selection=pixel_selection, resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") rtile, rmask = _postprocess_tile(tile, mask, rescale=rescale, color_ops=color_ops) if color_map: color_map = get_colormap(color_map, format="gdal") driver = "jpeg" if ext == "jpg" else ext 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))
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))
async def cog_tile( z: int = Path( ..., ge=0, le=30, description="Mercator tiles's zoom level"), x: int = Path(..., description="Mercator tiles's column"), y: int = Path(..., description="Mercator tiles's row"), TileMatrixSetId: TileMatrixSetNames = Query( TileMatrixSetNames.WebMercatorQuad, # type: ignore description="TileMatrixSet Name (default: 'WebMercatorQuad')", ), scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."), format: ImageType = Query( None, description="Output image type. Default is auto."), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), image_params: CommonTileParams = Depends(), cache_client: CacheLayer = Depends(utils.get_cache), request_id: str = Depends(request_hash), ): """Create map tile from a COG.""" timings = [] headers: Dict[str, str] = {} tilesize = scale * 256 tms = morecantile.tms.get(TileMatrixSetId.name) content = None if cache_client: try: content, ext = cache_client.get_image_from_cache(request_id) format = ImageType[ext] headers["X-Cache"] = "HIT" except Exception: content = None if not content: with utils.Timer() as t: with COGReader(url, tms=tms) as cog: tile, mask = cog.tile( x, y, z, tilesize=tilesize, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, **image_params.kwargs, ) colormap = image_params.color_map or cog.colormap timings.append(("Read", t.elapsed)) if not format: format = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: tile = utils.postprocess( tile, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: if format == ImageType.npy: sio = BytesIO() numpy.save(sio, (tile, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[format.value] options = img_profiles.get(driver.lower(), {}) if format == ImageType.tif: bounds = tms.xy_bounds(x, y, z) dst_transform = from_bounds(*bounds, tilesize, tilesize) options = {"crs": tms.crs, "transform": dst_transform} content = render(tile, mask, img_format=driver, colormap=colormap, **options) timings.append(("Format", t.elapsed)) if cache_client and content: cache_client.set_image_cache(request_id, (content, format.value)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
def _tile( z: int, x: int, y: int, scale: int = 1, ext: str = None, url: str = None, indexes: Optional[Union[str, Tuple]] = None, expr: Optional[str] = None, nodata: Optional[Union[str, int, float]] = None, rescale: Optional[str] = None, color_formula: Optional[str] = None, color_map: Optional[str] = None, resampling_method: str = "bilinear", **kwargs, ) -> Tuple[str, str, bytes]: """Handle /tiles requests.""" if indexes and expr: raise TilerError("Cannot pass indexes and expression") if not url: raise TilerError("Missing 'url' parameter") if nodata is not None: nodata = numpy.nan if nodata == "nan" else float(nodata) tilesize = scale * 256 if isinstance(indexes, str): indexes = tuple(map(int, indexes.split(","))) with rasterio.Env(aws_session): with COGReader(url) as cog: tile, mask = cog.tile( x, y, z, tilesize=tilesize, indexes=indexes, expression=expr, nodata=nodata, resampling_method=resampling_method, **kwargs, ) color_map = cmap.get(color_map) if color_map else cog.colormap if not ext: ext = "jpg" if mask.all() else "png" tile = utils.postprocess(tile, mask, rescale=rescale, color_formula=color_formula) if ext == "npy": sio = io.BytesIO() numpy.save(sio, (tile, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[ext] options = img_profiles.get(driver.lower(), {}) if ext == "tif": options = geotiff_options(x, y, z, tilesize=tilesize) if color_map: options["colormap"] = color_map content = render(tile, mask, img_format=driver, **options) return ("OK", mimetype[ext], content)
def _img( mosaicid: str = None, z: int = None, x: int = None, y: int = None, scale: int = 1, ext: str = None, url: str = None, indexes: Optional[Sequence[int]] = None, rescale: str = None, color_ops: str = None, color_map: str = None, pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple: """Handle tile requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.tile(x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") if indexes is not None and isinstance(indexes, str): indexes = list(map(int, indexes.split(","))) tilesize = 256 * scale if pixel_selection == "last": pixel_selection = "first" assets = list(reversed(assets)) with rasterio.Env(aws_session): pixsel_method = PIXSEL_METHODS[pixel_selection] tile, mask = mosaic_tiler( assets, x, y, z, cogeoTiler, indexes=indexes, tilesize=tilesize, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") rtile = _postprocess(tile, mask, rescale=rescale, color_formula=color_ops) if not ext: ext = "jpg" if mask.all() else "png" driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" options = geotiff_options(x, y, z, tilesize) if color_map: options["colormap"] = cmap.get(color_map) return ( "OK", f"image/{ext}", render(rtile, mask, img_format=driver, **options), )
def _img( mosaicid: str = None, z: int = None, x: int = None, y: int = None, scale: int = 1, ext: str = None, url: str = None, indexes: str = None, rescale: str = None, color_ops: str = None, color_map: str = None, pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") assets = fetch_and_find_assets(url, x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") if indexes: indexes = list(map(int, indexes.split(","))) tilesize = 256 * scale if pixel_selection == "last": pixel_selection = "first" assets = list(reversed(assets)) with rasterio.Env(aws_session): pixsel_method = PIXSEL_METHODS[pixel_selection] tile, mask = mosaic_tiler( assets, x, y, z, cogeoTiler, indexes=indexes, tilesize=tilesize, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") rtile = _postprocess(tile, mask, rescale=rescale, color_formula=color_ops) if color_map: if color_map.startswith("custom_"): color_map = get_custom_cmap(color_map) else: color_map = get_colormap(color_map, format="gdal") if not ext: ext = "jpg" if mask.all() else "png" driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) options = dict( crs={"init": "EPSG:3857"}, transform=from_bounds(*tile_bounds, tilesize, tilesize), ) return ( "OK", f"image/{ext}", array_to_image(rtile, mask, 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 rgb_tile = 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') boundaries_feature = self.request.query_params.get('boundaries') if boundaries_feature == '': boundaries_feature = None if boundaries_feature is not None: try: boundaries_feature = json.loads(boundaries_feature) except json.JSONDecodeError: raise exceptions.ValidationError( _("Invalid boundaries parameter")) 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, _discard_ = 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 == 'orthophoto' and rescale is None: rescale = "0,255" 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 COGReader(url) as src: if not src.tile_exists(z, x, y): raise exceptions.NotFound(_("Outside of bounds")) with COGReader(url) as src: minzoom, maxzoom = get_zoom_safe(src) has_alpha = has_alpha_band(src.dataset) if z < minzoom - ZOOM_EXTRA_LEVELS or z > maxzoom + ZOOM_EXTRA_LEVELS: raise exceptions.NotFound() if boundaries_feature is not None: try: boundaries_cutline = create_cutline( src.dataset, boundaries_feature, CRS.from_string('EPSG:4326')) except: raise exceptions.ValidationError(_("Invalid boundaries")) else: boundaries_cutline = None # Handle N-bands datasets for orthophotos (not plant health) if tile_type == 'orthophoto' and expr is None: ci = src.dataset.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.dataset) # Workaround for https://github.com/OpenDroneMap/WebODM/issues/894 if nodata is None and tile_type == 'orthophoto': nodata = 0 resampling = "nearest" padding = 0 if tile_type in ["dsm", "dtm"]: resampling = "bilinear" padding = 16 try: with COGReader(url) as src: if expr is not None: if boundaries_cutline is not None: tile = src.tile( x, y, z, expression=expr, tilesize=tilesize, nodata=nodata, padding=padding, resampling_method=resampling, vrt_options={'cutline': boundaries_cutline}) else: tile = src.tile(x, y, z, expression=expr, tilesize=tilesize, nodata=nodata, padding=padding, resampling_method=resampling) else: if boundaries_cutline is not None: tile = src.tile( x, y, z, tilesize=tilesize, nodata=nodata, padding=padding, resampling_method=resampling, vrt_options={'cutline': boundaries_cutline}) else: tile = src.tile(x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata, padding=padding, resampling_method=resampling) except TileOutsideBounds: raise exceptions.NotFound(_("Outside of bounds")) if color_map: try: colormap.get(color_map) except InvalidColorMapName: raise exceptions.ValidationError( _("Not a valid color_map value")) intensity = None try: rescale_arr = list(map(float, rescale.split(","))) except ValueError: raise exceptions.ValidationError(_("Invalid rescale value")) options = img_profiles.get(driver, {}) 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.data.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.dataset.meta["transform"][0] * delta_scale dy = -src.dataset.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.data[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] if intensity is not None: rgb = tile.post_process(in_range=(rescale_arr, )) if colormap: rgb, _discard_ = apply_cmap(rgb.data, colormap.get(color_map)) if rgb.data.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) if rgb is not None: return HttpResponse(render(rgb, tile.mask, img_format=driver, **options), content_type="image/{}".format(ext)) if color_map is not None: return HttpResponse( tile.post_process(in_range=(rescale_arr, )).render( img_format=driver, colormap=colormap.get(color_map), **options), content_type="image/{}".format(ext)) return HttpResponse(tile.post_process(in_range=(rescale_arr, )).render( img_format=driver, **options), content_type="image/{}".format(ext))
def profile(self): """Return rio-tiler image default profile.""" return img_profiles.get(self._name_, {})
async def cog_part( minx: float = Path(..., description="Bounding box min X"), miny: float = Path(..., description="Bounding box min Y"), maxx: float = Path(..., description="Bounding box max X"), maxy: float = Path(..., description="Bounding box max Y"), format: ImageType = Query(None, description="Output image type."), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), image_params: CommonImageParams = Depends(), ): """Create image from part of a COG.""" timings = [] headers: Dict[str, str] = {} with utils.Timer() as t: with COGReader(url) as cog: data, mask = cog.part( [minx, miny, maxx, maxy], height=image_params.height, width=image_params.width, max_size=image_params.max_size, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, **image_params.kwargs, ) colormap = image_params.color_map or cog.colormap timings.append(("Read", t.elapsed)) with utils.Timer() as t: data = utils.postprocess( data, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: if format == ImageType.npy: sio = BytesIO() numpy.save(sio, (data, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[format.value] options = img_profiles.get(driver.lower(), {}) content = render(data, mask, img_format=driver, colormap=colormap, **options) timings.append(("Format", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
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 tiles( z, x, y, scale=1, ext="png", url=None, nodata=None, indexes=None, rescale=None, color_ops=None, color_map=None, dem=None, ): """ Handle Raster /tiles requests. Note: All the querystring parameters are translated to function keywords and passed as string value by lambda_proxy Attributes ---------- z : int, required Mercator tile ZOOM level. x : int, required Mercator tile X index. y : int, required Mercator tile Y index. scale : int Output scale factor (default: 1). ext : str Image format to return (default: png). url : str, required Dataset url to read from. indexes : str, optional, (defaults: None) Comma separated band index number (e.g "1,2,3"). nodata, str, optional Custom nodata value if not preset in dataset. rescale : str, optional Min and Max data bounds to rescale data from. color_ops : str, optional rio-color compatible color formula color_map : str, optional Rio-tiler compatible colormap name ("cfastie" or "schwarzwald") dem : str, optional Create Mapbox or Mapzen RGBA encoded elevation image Returns ------- status : str Status of the request (e.g. OK, NOK). MIME type : str response body MIME type (e.g. image/jpeg). body : bytes Image body. """ if not url: raise TilerError("Missing 'url' parameter") if indexes: indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) if nodata is not None and isinstance(nodata, str): nodata = numpy.nan if nodata == "nan" else float(nodata) tilesize = 256 * scale tile, mask = main.tile(url, x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata) 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(tile, mask, rescale=rescale, color_ops=color_ops) if color_map: color_map = get_colormap(color_map, format="gdal") driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) return ( "OK", f"image/{ext}", array_to_image(rtile, rmask, img_format=driver, color_map=color_map, **options), )