Exemplo n.º 1
0
def test_reader_point():
    """Test STACReader.point."""
    lat = 32
    lon = 23.7

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

        with pytest.raises(Exception):
            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
Exemplo n.º 2
0
def test_reader_info():
    """Test STACReader.info."""
    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidBandName):
            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"]
Exemplo n.º 3
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}
Exemplo n.º 4
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}
Exemplo n.º 5
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
Exemplo n.º 6
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
Exemplo n.º 7
0
def test_reader_part():
    """Test STACReader.part."""
    bbox = (23.7, 31.506, 24.1, 32.514)

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

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

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

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

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

    # 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, 371)
    assert mask.shape == (1024, 371)

    # 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, 371)
    assert mask.shape == (1024, 371)

    with STACReader(STAC_PATH) as stac:
        data, mask = stac.part(bbox, assets="B04", max_size=None)
    assert data.shape == (1, 1129, 408)
    assert mask.shape == (1129, 408)
Exemplo n.º 8
0
def test_reader_tiles():
    """Test STACReader.tile."""
    tile = morecantile.Tile(z=9, x=289, y=207)

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

        with pytest.raises(Exception):
            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)
Exemplo n.º 9
0
def test_reader_preview():
    """Test STACReader.preview."""
    with STACReader(STAC_PATH) as stac:
        with pytest.raises(InvalidBandName):
            stac.preview(assets="B1")

        with pytest.raises(Exception):
            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)
Exemplo n.º 10
0
def mock_STACreader(src_path: str, *args, **kwargs) -> COGReader:
    """Mock rasterio.open."""
    prefix = os.path.join(os.path.dirname(__file__), "fixtures")
    assert src_path.startswith("https://myurl.com/")
    stac_path = os.path.basename(src_path)
    return STACReader(os.path.join(prefix, stac_path), *args, **kwargs)
Exemplo n.º 11
0
def test_fetch_stac(requests, s3_get):
    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
    requests.assert_not_called()
    s3_get.assert_not_called()

    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
    requests.assert_not_called()
    s3_get.assert_not_called()

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

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

    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",
        ]
    requests.assert_not_called()
    s3_get.assert_not_called()

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

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

    # Should raise an error in future versions
    with STACReader(STAC_PATH, include_assets={"B1"}) as stac:
        assert not stac.assets
    requests.assert_not_called()
    s3_get.assert_not_called()

    # HTTP
    class MockResponse:
        def __init__(self, data):
            self.data = data

        def json(self):
            return json.loads(self.data)

    with open(STAC_PATH, "r") as f:
        requests.get.return_value = MockResponse(f.read())

    with STACReader(
        "http://somewhereovertherainbow.io/mystac.json", include_assets={"B01"}
    ) as stac:
        assert stac.assets == ["B01"]
    requests.get.assert_called_once()
    s3_get.assert_not_called()
    requests.mock_reset()

    # S3
    with open(STAC_PATH, "r") as f:
        s3_get.return_value = f.read()

    with STACReader(
        "s3://somewhereovertherainbow.io/mystac.json", include_assets={"B01"}
    ) as stac:
        assert stac.assets == ["B01"]
    requests.assert_not_called()
    s3_get.assert_called_once()
Exemplo n.º 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
Exemplo n.º 13
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:
        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,
    )
Exemplo n.º 14
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))

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