Example #1
0
async def stac_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="STAC Item URL."),
    assets: str = Query("", description="comma (,) separated list of asset names."),
    image_params: CommonImageParams = Depends(),
):
    """Create image from part of STAC assets."""
    timings = []
    headers: Dict[str, str] = {}

    if not image_params.expression and not assets:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Must pass Expression or Asset list.",
        )

    with utils.Timer() as t:
        with STACReader(url) as stac:
            data, mask = stac.part(
                [minx, miny, maxx, maxy],
                height=image_params.height,
                width=image_params.width,
                max_size=image_params.max_size,
                assets=assets.split(","),
                expression=image_params.expression,
                indexes=image_params.indexes,
                nodata=image_params.nodata,
                **image_params.kwargs,
            )
    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:
        content = utils.reformat(
            data, mask, img_format=format, colormap=image_params.color_map
        )
    timings.append(("Format", t.elapsed))
    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,
    )
Example #2
0
async def mosaic_point(
    lon: float = Path(..., description="Longitude"),
    lat: float = Path(..., description="Latitude"),
    bidx: Optional[str] = Query(
        None, title="Band indexes", description="comma (',') delimited band indexes",
    ),
    expression: Optional[str] = Query(
        None,
        title="Band Math expression",
        description="rio-tiler's band math expression (e.g B1/B2)",
    ),
    mosaic_path: str = Depends(MosaicPath),
):
    """Get Point value for a MosaicJSON."""
    indexes = tuple(int(s) for s in re.findall(r"\d+", bidx)) if bidx else None

    timings = []
    headers: Dict[str, str] = {}

    with utils.Timer() as t:
        with MosaicBackend(mosaic_path) as mosaic:
            assets = mosaic.point(lon, lat)

    timings.append(("Read-mosaic", t.elapsed))

    # Rio-tiler provides a helper function (``rio_tiler.reader.multi_point``) for reading a point from multiple assets
    # using an external threadpool.  For similar reasons as described below, we will transcribe the rio-tiler code to
    # use the default executor provided by the event loop.
    futures = [
        run_in_threadpool(
            _read_point, asset, lon, lat, indexes=indexes, expression=expression
        )
        for asset in assets
    ]

    values = []
    with utils.Timer() as t:
        async for fut in _process_futures(
            futures, concurrency=int(os.getenv("MOSAIC_CONCURRENCY", 10))
        ):
            try:
                values.append(await fut)
            except Exception:
                continue

    timings.append(("Read-tiles", t.elapsed))

    if timings:
        headers["X-Server-Timings"] = "; ".join(
            ["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings]
        )

    return {"coordinates": [lon, lat], "values": values}
Example #3
0
async def cog_point(
    lon: float = Path(..., description="Longitude"),
    lat: float = Path(..., description="Latitude"),
    url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
    assets: str = Query(
        "", description="comma (,) separated list of asset names."),
    expression: Optional[str] = Query(
        None,
        title="Band Math expression",
        description="rio-tiler's band math expression (e.g B1/B2)",
    ),
    bidx: Optional[str] = Query(
        None,
        title="Band indexes",
        description="comma (',') delimited band indexes",
    ),
    asset_expression: Optional[str] = Query(
        None,
        title="Band Math expression for assets bands",
        description="rio-tiler's band math expression (e.g B1/B2)",
    ),
):
    """Get Point value for a COG."""
    if not expression and not assets:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Must pass Expression or Asset list.",
        )

    indexes = tuple(int(s) for s in re.findall(r"\d+", bidx)) if bidx else None

    timings = []
    headers: Dict[str, str] = {}

    with utils.Timer() as t:
        with STACReader(url) as stac:
            values = stac.point(
                lon,
                lat,
                assets=assets,
                expression=expression,
                indexes=indexes,
                asset_expression=asset_expression,
            )
    timings.append(("Read", t.elapsed))

    if timings:
        headers["X-Server-Timings"] = "; ".join([
            "{} - {:0.2f}".format(name, time * 1000)
            for (name, time) in timings
        ])

    return {"coordinates": [lon, lat], "values": values}
Example #4
0
async def cog_point(
    lon: float = Path(..., description="Longitude"),
    lat: float = Path(..., description="Latitude"),
    url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
    bidx: Optional[str] = Query(
        None,
        title="Band indexes",
        description="comma (',') delimited band indexes",
    ),
    expression: Optional[str] = Query(
        None,
        title="Band Math expression",
        description="rio-tiler's band math expression (e.g B1/B2)",
    ),
):
    """Get Point value for a COG."""
    indexes = tuple(int(s) for s in re.findall(r"\d+", bidx)) if bidx else None

    timings = []
    headers: Dict[str, str] = {}

    with utils.Timer() as t:
        with COGReader(url) as cog:
            values = cog.point(lon,
                               lat,
                               indexes=indexes,
                               expression=expression)
    timings.append(("Read", t.elapsed))

    if timings:
        headers["X-Server-Timings"] = "; ".join([
            "{} - {:0.2f}".format(name, time * 1000)
            for (name, time) in timings
        ])

    return {"coordinates": [lon, lat], "values": values}
Example #5
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,
    )
Example #6
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,
    )
Example #7
0
async def mosaic_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..."
    ),
    format: ImageType = Query(None, description="Output image type. Default is auto."),
    pixel_selection: PixelSelectionMethod = Query(
        PixelSelectionMethod.first, description="Pixel selection method."
    ),
    image_params: CommonTileParams = Depends(),
    mosaic_path: str = Depends(MosaicPath),
):
    """Read MosaicJSON tile"""
    pixsel = pixel_selection.method()
    timings = []
    headers: Dict[str, str] = {}

    with utils.Timer() as t:
        with MosaicBackend(mosaic_path) as mosaic:
            assets = mosaic.tile(x=x, y=y, z=z)
            if not assets:
                raise TileNotFoundError(f"No assets found for tile {z}/{x}/{y}")
    timings.append(("Read-mosaic", t.elapsed))

    tilesize = 256 * scale

    # Rio-tiler-mosaic uses an external ThreadPoolExecutor to process multiple assets at once but we want to use the
    # executor provided by the event loop.  Instead of calling ``rio_tiler_mosaic.mosaic.mosaic_tiler`` directly we will
    # transcribe the code here and use the executor provided by the event loop.  This also means we define this function
    # as a coroutine (even though nothing that is called is a coroutine), since the event loop's executor isn't
    # available in normal ``def`` functions.
    futures = [
        run_in_threadpool(
            _read_tile,
            asset,
            x,
            y,
            z,
            tilesize=tilesize,
            indexes=image_params.indexes,
            expression=image_params.expression,
            nodata=image_params.nodata,
            **image_params.kwargs,
        )
        for asset in assets
    ]

    with utils.Timer() as t:
        async for fut in _process_futures(
            futures, concurrency=int(os.getenv("MOSAIC_CONCURRENCY", 10))
        ):
            try:
                tile, mask = await fut
            except Exception:
                # Gracefully handle exceptions
                continue

            tile = numpy.ma.array(tile)
            tile.mask = mask == 0
            pixsel.feed(tile)
            if pixsel.is_done:
                break
    timings.append(("Read-tiles", t.elapsed))

    tile, mask = pixsel.data
    if tile is None:
        raise TileNotFoundError(f"Tile {z}/{x}/{y} was not found")

    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))

    opts = {}
    if ImageType.tif in format:
        opts = geotiff_options(x, y, z, tilesize=tilesize)

    with utils.Timer() as t:
        content = utils.reformat(
            tile, mask, img_format=format, colormap=image_params.color_map, **opts
        )
    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
    )
Example #8
0
async def stac_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="STAC Item URL."),
    assets: str = Query("", description="comma (,) separated list of asset names."),
    image_params: CommonTileParams = Depends(),
    cache_client: CacheLayer = Depends(utils.get_cache),
    request_id: str = Depends(request_hash),
):
    """Create map tile from a STAC item."""
    timings = []
    headers: Dict[str, str] = {}

    if not image_params.expression and not assets:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Must pass Expression or Asset list.",
        )

    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 STACReader(url, tms=tms) as stac:
                tile, mask = stac.tile(
                    x,
                    y,
                    z,
                    assets=assets.split(","),
                    tilesize=tilesize,
                    indexes=image_params.indexes,
                    expression=image_params.expression,
                    nodata=image_params.nodata,
                )
        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))

        bounds = tms.xy_bounds(x, y, z)
        dst_transform = from_bounds(*bounds, tilesize, tilesize)
        with utils.Timer() as t:
            content = utils.reformat(
                tile,
                mask,
                img_format=format,
                colormap=image_params.color_map,
                transform=dst_transform,
                crs=tms.crs,
            )
        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,
    )
Example #9
0
async def stac_preview(
        format: ImageType = Query(
            None, description="Output image type. Default is auto."),
        url: str = Query(..., description="STAC Item URL."),
        assets: str = Query(
            "", description="comma (,) separated list of asset names."),
        image_params: CommonImageParams = Depends(),
):
    """Create preview of STAC assets."""
    timings = []
    headers: Dict[str, str] = {}

    if not image_params.expression and not assets:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Must pass Expression or Asset list.",
        )

    with utils.Timer() as t:
        with STACReader(url) as stac:
            data, mask = stac.preview(
                assets=assets.split(","),
                expression=image_params.expression,
                height=image_params.height,
                width=image_params.width,
                max_size=image_params.max_size,
                indexes=image_params.indexes,
                nodata=image_params.nodata,
                **image_params.kwargs,
            )
    timings.append(("Read", t.elapsed))

    if not format:
        format = ImageType.jpg if mask.all() else ImageType.png

    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=image_params.color_map,
                **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,
    )