Esempio n. 1
0
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")
Esempio n. 2
0
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),
    )
Esempio n. 3
0
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
Esempio n. 4
0
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),
    )
Esempio n. 5
0
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),
    )
Esempio n. 6
0
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),
    )
Esempio n. 7
0
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"}
Esempio n. 8
0
        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])
Esempio n. 9
0
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)
Esempio n. 10
0
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),
    )
Esempio n. 11
0
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),
    )
Esempio n. 12
0
    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))
Esempio n. 13
0
        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}")
Esempio n. 14
0
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])
Esempio n. 15
0
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),
    )
Esempio n. 16
0
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)
Esempio n. 17
0
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),
    )
Esempio n. 18
0
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)
Esempio n. 19
0
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),
    )
Esempio n. 20
0
    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))
Esempio n. 21
0
    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))
Esempio n. 22
0
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,
    )
Esempio n. 23
0
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)
Esempio n. 24
0
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),
    )
Esempio n. 25
0
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),
    )
Esempio n. 26
0
    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))
Esempio n. 27
0
 def profile(self):
     """Return rio-tiler image default profile."""
     return img_profiles.get(self._name_, {})
Esempio n. 28
0
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,
    )
Esempio n. 29
0
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),
    )
Esempio n. 30
0
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),
    )