Пример #1
0
def test_fetch_stac():
    with STACReader(STAC_PATH, include_asset_types=None) as stac:
        assert stac.minzoom == 0
        assert stac.maxzoom == 24
        assert stac.tms.identifier == "WebMercatorQuad"
        assert stac.filepath == STAC_PATH
        assert stac.assets == ALL_ASSETS

    with STACReader(STAC_PATH) as stac:
        assert stac.minzoom == 0
        assert stac.maxzoom == 24
        assert stac.tms.identifier == "WebMercatorQuad"
        assert stac.filepath == STAC_PATH
        assert "metadata" not in stac.assets
        assert "thumbnail" not in stac.assets
        assert "info" not in stac.assets

    with STACReader(STAC_PATH, include_assets={"B01", "B02"}) as stac:
        assert stac.assets == ["B01", "B02"]

    with STACReader(STAC_PATH, include_assets={"B01", "B02"}) as stac:
        assert stac.assets == ["B01", "B02"]

    with STACReader(STAC_PATH,
                    exclude_assets={"overview", "visual", "AOT", "WVP",
                                    "SCL"}) as stac:
        assert stac.assets == [
            "B01",
            "B02",
            "B03",
            "B04",
            "B05",
            "B06",
            "B07",
            "B08",
            "B8A",
            "B09",
            "B11",
            "B12",
        ]

    with STACReader(STAC_PATH,
                    include_asset_types={"application/xml"}) as stac:
        assert stac.assets == ["metadata"]

    with STACReader(
            STAC_PATH,
            include_asset_types={"application/xml", "image/png"},
            include_assets={"metadata", "overview"},
    ) as stac:
        assert stac.assets == ["metadata"]

    with STACReader(STAC_PATH,
                    tms=morecantile.tms.get("WorldCRS84Quad")) as stac:
        assert stac.minzoom == 0
        assert stac.maxzoom == 17
        tms = stac.reader_options.get("tms")
        assert tms.identifier == "WorldCRS84Quad"
Пример #2
0
async def stac_bounds(
    resp: Response, url: str = Query(..., description="STAC item URL."),
):
    """Return the bounds of the STAC item."""
    resp.headers["Cache-Control"] = "max-age=3600"
    with STACReader(url) as stac:
        return {"bounds": stac.bounds}
Пример #3
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,
    )
Пример #4
0
def test_reader_info(rio):
    """Test STACReader.info."""
    rio.open = mock_rasterio_open

    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidAssetName):
            stac.info(assets="B1")

        data = stac.info(assets="B01")
        assert len(data.keys()) == 1
        assert data["B01"]

    with STACReader(STAC_PATH) as stac:
        data = stac.info(assets=["B04", "B02"])
        assert len(data.keys()) == 2
        assert data["B02"]
        assert data["B04"]
Пример #5
0
def test_reader_point(rio):
    """Test STACReader.point."""
    rio.open = mock_rasterio_open

    lat = 32
    lon = 23.7

    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidAssetName):
            stac.point(lon, lat, assets="B1")

        with pytest.raises(MissingAssets):
            stac.point(lon, lat)

        data = stac.point(lon, lat, assets="B01")
        assert len(data) == 1

    with STACReader(STAC_PATH) as stac:
        data = stac.point(lon, lat, expression="B04/B02")
        assert len(data) == 1

    with STACReader(STAC_PATH) as stac:
        data = stac.point(lon, lat, assets=["B04", "B02"])
        assert len(data) == 2

    # This is possible but user should not to it ;-)
    # We are reading B01 and B02 and telling rasterio to return twice bidx 1.
    with STACReader(STAC_PATH) as stac:
        data = stac.point(lon, lat, assets=["B04", "B02"], indexes=(1, 1))
        assert len(data) == 2
        assert len(data[0]) == 2

    # Power User might use expression for each assets
    with STACReader(STAC_PATH) as stac:
        data = stac.point(lon,
                          lat,
                          assets=["B04", "B02"],
                          asset_expression="b1/2")
        assert len(data) == 2
Пример #6
0
async def stac_info(
    resp: Response,
    url: str = Query(..., description="STAC item URL."),
    assets: str = Query(None, description="comma (,) separated list of asset names."),
):
    """Return basic info on STAC item's COG."""
    resp.headers["Cache-Control"] = "max-age=3600"
    with STACReader(url) as stac:
        if not assets:
            return stac.assets

        info = stac.info(assets.split(","))

    return info
Пример #7
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}
Пример #8
0
def test_reader_tiles(rio):
    """Test STACReader.tile."""
    rio.open = mock_rasterio_open

    tile = morecantile.Tile(z=9, x=289, y=207)

    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidAssetName):
            stac.tile(*tile, assets="B1")

        with pytest.raises(MissingAssets):
            stac.tile(*tile)

        data, mask = stac.tile(*tile, assets="B01")
        assert data.shape == (1, 256, 256)
        assert mask.shape == (256, 256)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.tile(*tile, expression="B01/B02")
        assert data.shape == (1, 256, 256)
        assert mask.shape == (256, 256)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.tile(*tile, assets=["B01", "B02"])
        assert data.shape == (2, 256, 256)
        assert mask.shape == (256, 256)

    # This is possible but user should not to it ;-)
    # We are reading B01 and B02 and telling rasterio to return twice bidx 1.
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.tile(*tile, assets=["B01", "B02"], indexes=(1, 1))
        assert data.shape == (4, 256, 256)
        assert mask.shape == (256, 256)

    # Power User might use expression for each assets
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.tile(*tile,
                               assets=["B01", "B02"],
                               asset_expression="b1/2")
        assert data.shape == (2, 256, 256)
        assert mask.shape == (256, 256)

    with STACReader(STAC_PATH,
                    tms=morecantile.tms.get("WorldCRS84Quad")) as stac:
        data, mask = stac.tile(4, 1, 2, assets="B01")
        assert data.shape == (1, 256, 256)
        assert mask.shape == (256, 256)
Пример #9
0
def test_reader_part(rio):
    """Test STACReader.part."""
    rio.open = mock_rasterio_open

    bbox = (23.7, 31.506, 24.1, 32.514)

    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidAssetName):
            stac.part(bbox, assets="B1")

        with pytest.raises(MissingAssets):
            stac.part(bbox)

        data, mask = stac.part(bbox, assets="B01")
        assert data.shape == (1, 172, 69)
        assert mask.shape == (172, 69)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox, expression="B04/B02")
        assert data.shape == (1, 1024, 407)
        assert mask.shape == (1024, 407)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox, assets=["B04", "B02"])
        assert data.shape == (2, 1024, 407)
        assert mask.shape == (1024, 407)

    # This is possible but user should not to it ;-)
    # We are reading B01 and B02 and telling rasterio to return twice bidx 1.
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox, assets=["B04", "B02"], indexes=(1, 1))
        assert data.shape == (4, 1024, 407)
        assert mask.shape == (1024, 407)

    # Power User might use expression for each assets
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox,
                               assets=["B04", "B02"],
                               asset_expression="b1/2")
        assert data.shape == (2, 1024, 407)
        assert mask.shape == (1024, 407)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox, assets="B04", max_size=None)
        assert data.shape == (1, 1030, 409)
        assert mask.shape == (1030, 409)
Пример #10
0
async def stac_metadata(
    request: Request,
    resp: Response,
    url: str = Query(..., description="STAC item URL."),
    assets: str = Query(..., description="comma (,) separated list of asset names."),
    metadata_params: CommonMetadataParams = Depends(),
):
    """Return the metadata of the COG."""
    with STACReader(url) as stac:
        info = stac.metadata(
            assets.split(","),
            metadata_params.pmin,
            metadata_params.pmax,
            nodata=metadata_params.nodata,
            indexes=metadata_params.indexes,
            max_size=metadata_params.max_size,
            hist_options=metadata_params.hist_options,
            bounds=metadata_params.bounds,
            **metadata_params.kwargs,
        )

    resp.headers["Cache-Control"] = "max-age=3600"
    return info
Пример #11
0
def test_reader_preview(rio):
    """Test STACReader.preview."""
    rio.open = mock_rasterio_open

    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidAssetName):
            stac.preview(assets="B1")

        with pytest.raises(MissingAssets):
            stac.preview()

        data, mask = stac.preview(assets="B01")
        assert data.shape == (1, 183, 183)
        assert mask.shape == (183, 183)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.preview(expression="B04/B02")
        assert data.shape == (1, 1024, 1024)
        assert mask.shape == (1024, 1024)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.preview(assets=["B04", "B02"])
        assert data.shape == (2, 1024, 1024)
        assert mask.shape == (1024, 1024)

    # This is possible but user should not to it ;-)
    # We are reading B01 and B02 and telling rasterio to return twice bidx 1.
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.preview(assets=["B04", "B02"], indexes=(1, 1))
        assert data.shape == (4, 1024, 1024)
        assert mask.shape == (1024, 1024)

    # Power User might use expression for each assets
    with STACReader(STAC_PATH) as stac:
        data, mask = stac.preview(assets=["B04", "B02"],
                                  asset_expression="b1/2")
        assert data.shape == (2, 1024, 1024)
        assert mask.shape == (1024, 1024)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.preview(assets="B04", max_size=512)
        assert data.shape == (1, 512, 512)
        assert mask.shape == (512, 512)
Пример #12
0
async def stac_tilejson(
    request: Request,
    response: Response,
    TileMatrixSetId: TileMatrixSetNames = Query(
        TileMatrixSetNames.WebMercatorQuad,  # type: ignore
        description="TileMatrixSet Name (default: 'WebMercatorQuad')",
    ),
    url: str = Query(..., description="STAC Item 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)",
    ),
    tile_format: Optional[ImageType] = Query(
        None, description="Output image type. Default is auto."
    ),
    tile_scale: int = Query(
        1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."
    ),
    minzoom: Optional[int] = Query(None, description="Overwrite default minzoom."),
    maxzoom: Optional[int] = Query(None, description="Overwrite default maxzoom."),
):
    """Return a TileJSON document for a STAC item."""
    scheme = request.url.scheme
    host = request.headers["host"]

    kwargs = dict(request.query_params)
    kwargs.pop("tile_format", None)
    kwargs.pop("tile_scale", None)
    kwargs.pop("TileMatrixSetId", None)
    kwargs.pop("minzoom", None)
    kwargs.pop("maxzoom", None)

    if not expression and not assets:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Expression or Assets HAVE to be set in the queryString.",
        )

    qs = urlencode(list(kwargs.items()))
    if tile_format:
        tile_url = f"{scheme}://{host}/stac/tiles/{TileMatrixSetId.name}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}"
    else:
        tile_url = f"{scheme}://{host}/stac/tiles/{TileMatrixSetId.name}/{{z}}/{{x}}/{{y}}@{tile_scale}x?{qs}"

    tms = morecantile.tms.get(TileMatrixSetId.name)
    with STACReader(url, tms=tms) as stac:
        center = list(stac.center)
        if minzoom:
            center[-1] = minzoom
        tjson = {
            "bounds": stac.bounds,
            "center": tuple(center),
            "minzoom": minzoom or stac.minzoom,
            "maxzoom": maxzoom or stac.maxzoom,
            "name": os.path.basename(url),
            "tiles": [tile_url],
        }

    response.headers["Cache-Control"] = "max-age=3600"
    return tjson
Пример #13
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,
    )
Пример #14
0
def mock_STACreader(src_path: str, *args, **kwargs) -> COGReader:
    """Mock rasterio.open."""
    assert src_path.startswith("https://myurl.com/")
    stac_path = os.path.basename(src_path)
    return STACReader(os.path.join(DATA_DIR, stac_path), *args, **kwargs)