示例#1
0
def test_reader_part():
    """Test COGReader.part."""
    lon = -58.181
    lat = 73.8794
    zoom = 7
    tms = morecantile.tms.get("WebMercatorQuad")
    bounds = tms.bounds(tms.tile(lon, lat, zoom))

    with COGReader(COG_PATH) as cog:
        data, mask = cog.part(bounds, dst_crs=cog.dataset.crs)
    assert data.shape == (1, 896, 906)
    assert mask.shape == (896, 906)

    with COGReader(COG_PATH) as cog:
        data, mask = cog.part(bounds, indexes=(1, 1), dst_crs=cog.dataset.crs)
    assert data.shape == (2, 896, 906)
    assert mask.shape == (896, 906)

    with COGReader(COG_PATH) as cog:
        data, mask = cog.part(bounds, max_size=512, dst_crs=cog.dataset.crs)
    assert data.shape == (1, 507, 512)
    assert mask.shape == (507, 512)

    with COGReader(COG_PATH) as cog:
        data, mask = cog.part(bounds, expression="B1/2,B1+3", dst_crs=cog.dataset.crs)
    assert data.shape == (2, 896, 906)
    assert mask.shape == (896, 906)
示例#2
0
def test_reader_stats():
    """Test COGReader.stats."""
    with COGReader(COG_PATH) as cog:
        data = cog.stats()
    assert data[1]["min"] == 1
    assert data[1]["max"] == 7872

    with COGReader(COG_CMAP_PATH) as cog:
        data = cog.stats()
    assert data[1]["histogram"][1] == [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
    ]
示例#3
0
def test_reader_info():
    """Test COGReader.info."""
    with COGReader(COG_PATH) as cog:
        info = cog.info()
    assert info["minzoom"] == 5
    assert info["maxzoom"] == 8
    assert info["center"][-1] == 5
    assert info["bounds"]
    assert info["band_metadata"]
    assert info["band_descriptions"]
    assert info["dtype"]
    assert info["colorinterp"]
    assert info["nodata_type"]
    assert not info.get("colormap")
    assert not info.get("scale")
    assert not info.get("offset")

    with COGReader(COG_CMAP_PATH) as cog:
        info = cog.info()
    assert info["colormap"]

    with COGReader(COG_SCALE_PATH) as cog:
        info = cog.info()
    assert info["scale"]
    assert info["offset"]
示例#4
0
def test_reader_get_zoom():
    """Test COGReader._get_zooms function with different projection."""
    with COGReader(COG_PATH) as cog:
        assert cog.minzoom == 5
        assert cog.maxzoom == 8

    tms = morecantile.tms.get("WorldCRS84Quad")
    with COGReader(COG_PATH, tms=tms) as cog:
        assert cog.minzoom == 4
        assert cog.maxzoom == 8
示例#5
0
def test_reader_metadata():
    """Test COGReader.info."""
    with COGReader(COG_PATH) as cog:
        info = cog.metadata()
    assert info["band_metadata"]
    assert info["band_descriptions"]
    assert info["statistics"][1]
示例#6
0
def test_reader_point():
    """Test COGReader.point."""
    lon = -58.181
    lat = 73.8794

    with COGReader(COG_PATH) as cog:
        data = cog.point(lon, lat)
    assert len(data) == 1

    with COGReader(COG_PATH) as cog:
        data = cog.point(lon, lat, indexes=(1, 1))
    assert len(data) == 2

    with COGReader(COG_PATH) as cog:
        data = cog.point(lon, lat, expression="B1/2,B1+3")
    assert len(data) == 2
示例#7
0
async def bounds(
    resp: Response, url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
):
    """Handle /bounds requests."""
    resp.headers["Cache-Control"] = "max-age=3600"
    with COGReader(url) as cog:
        return {"bounds": cog.bounds}
示例#8
0
def wtms(
    request: Request,
    response: Response,
    TileMatrixSetId: TileMatrixSetNames = Query(
        TileMatrixSetNames.WebMercatorQuad,  # type: ignore
        description="TileMatrixSet Name (default: 'WebMercatorQuad')",
    ),
    url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
    tile_format: ImageType = Query(
        ImageType.png, description="Output image type. Default is png."),
    tile_scale: int = Query(
        1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."),
):
    """OGC WMTS endpoit."""
    scheme = request.url.scheme
    host = request.headers["host"]
    endpoint = f"{scheme}://{host}/cog"

    kwargs = dict(request.query_params)
    kwargs.pop("tile_format", None)
    kwargs.pop("tile_scale", None)
    qs = urlencode(list(kwargs.items()))

    tms = morecantile.tms.get(TileMatrixSetId.name)
    with COGReader(url, tms=tms) as cog:
        minzoom, maxzoom, bounds = cog.minzoom, cog.maxzoom, cog.bounds

    media_type = ImageMimeTypes[tile_format.value].value

    tileMatrix = []
    for zoom in range(minzoom, maxzoom + 1):
        matrix = tms.matrix(zoom)
        tm = f"""
                <TileMatrix>
                    <ows:Identifier>{matrix.identifier}</ows:Identifier>
                    <ScaleDenominator>{matrix.scaleDenominator}</ScaleDenominator>
                    <TopLeftCorner>{matrix.topLeftCorner[0]} {matrix.topLeftCorner[1]}</TopLeftCorner>
                    <TileWidth>{matrix.tileWidth}</TileWidth>
                    <TileHeight>{matrix.tileHeight}</TileHeight>
                    <MatrixWidth>{matrix.matrixWidth}</MatrixWidth>
                    <MatrixHeight>{matrix.matrixHeight}</MatrixHeight>
                </TileMatrix>"""
        tileMatrix.append(tm)

    tile_ext = f"@{tile_scale}x.{tile_format.value}"
    return templates.TemplateResponse(
        "wmts.xml",
        {
            "request": request,
            "endpoint": endpoint,
            "bounds": bounds,
            "tileMatrix": tileMatrix,
            "tms": tms,
            "title": "Cloud Optimized GeoTIFF",
            "query_string": qs,
            "tile_format": tile_ext,
            "media_type": media_type,
        },
        media_type=MimeTypes.xml.value,
    )
示例#9
0
def _wmts(
        request: Request,
        response: Response,
        identifier: str = Path("WebMercatorQuad", title="TMS identifier"),
        filename: str = Query(...),
):
    """Handle /tiles requests."""
    tms = morecantile.tms.get(identifier)

    host = request.headers["host"]
    scheme = request.url.scheme
    endpoint = f"{scheme}://{host}"

    with COGReader(f"{filename}.tif", tms=tms) as cog:  # type: ignore
        meta = cog.info

        return XMLResponse(
            ogc_wmts(
                endpoint,
                tms,
                bounds=meta["bounds"],
                query_string=f"filename={filename}",
                minzoom=meta["minzoom"],
                maxzoom=meta["maxzoom"],
                title=os.path.basename(filename),
            ))
示例#10
0
async def cog_info(resp: Response,
                   url: str = Query(
                       ..., description="Cloud Optimized GeoTIFF URL.")):
    """Return basic info on COG."""
    resp.headers["Cache-Control"] = "max-age=3600"
    with COGReader(url) as cog:
        info = cog.info
    return info
示例#11
0
def test_reader_preview():
    """Test COGReader.preview."""

    with COGReader(COG_PATH) as cog:
        data, mask = cog.preview()
    assert data.shape == (1, 1024, 1021)
    assert mask.shape == (1024, 1021)

    with COGReader(COG_PATH) as cog:
        data, mask = cog.preview(indexes=(1, 1))
    assert data.shape == (2, 1024, 1021)
    assert mask.shape == (1024, 1021)

    with COGReader(COG_PATH) as cog:
        data, mask = cog.preview(expression="B1/2,B1+3")
    assert data.shape == (2, 1024, 1021)
    assert mask.shape == (1024, 1021)
示例#12
0
async def metadata(
    request: Request,
    resp: Response,
    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."
    ),
    pmin: float = Query(2.0, description="Minimum percentile"),
    pmax: float = Query(98.0, description="Maximum percentile"),
    max_size: int = Query(1024, description="Maximum image size to read onto."),
    histogram_bins: Optional[int] = None,
    histogram_range: Optional[str] = Query(
        None, description="Coma (',') delimited Min,Max bounds"
    ),
):
    """Handle /metadata requests."""
    kwargs = dict(request.query_params)
    kwargs.pop("url", None)
    kwargs.pop("bidx", None)
    kwargs.pop("nodata", None)
    kwargs.pop("pmin", None)
    kwargs.pop("pmax", None)
    kwargs.pop("max_size", None)
    kwargs.pop("histogram_bins", None)
    kwargs.pop("histogram_range", None)

    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)

    hist_options: Dict[str, Any] = dict()
    if histogram_bins:
        hist_options.update(dict(bins=histogram_bins))
    if histogram_range:
        hist_options.update(dict(range=list(map(float, histogram_range.split(",")))))

    with COGReader(url) as cog:
        info = cog.info
        info.pop("maxzoom", None)  # We don't use TMS here
        info.pop("minzoom", None)  # We don't use TMS here
        info.pop("center", None)  # We don't use TMS here
        stats = cog.stats(
            pmin,
            pmax,
            nodata=nodata,
            indexes=indexes,
            max_size=max_size,
            hist_options=hist_options,
            **kwargs,
        )
        info["statistics"] = stats

    resp.headers["Cache-Control"] = "max-age=3600"
    return info
示例#13
0
async def info(
    resp: Response, url: str = Query(..., description="Cloud Optimized GeoTIFF URL.")
):
    """Handle /info requests."""
    resp.headers["Cache-Control"] = "max-age=3600"
    with COGReader(url) as cog:
        info = cog.info
    info.pop("maxzoom", None)  # We don't use TMS here
    info.pop("minzoom", None)  # We don't use TMS here
    info.pop("center", None)  # We don't use TMS here
    return info
示例#14
0
def test_COGReader_Options():
    """Set options in reader."""
    with COGReader(COG_PATH, nodata=1) as cog:
        meta = cog.metadata()
        assert meta["statistics"][1]["pc"] == [2720, 6896]

    with COGReader(COG_PATH, nodata=1) as cog:
        _, mask = cog.tile(43, 25, 7)
        assert not mask.all()

    with COGReader(COG_SCALE_PATH, unscale=True) as cog:
        p = cog.point(310000, 4100000, coord_crs=cog.dataset.crs)
        assert round(p[0], 3) == 1000.892

        # passing unscale in method should overwrite the defaults
        p = cog.point(310000, 4100000, coord_crs=cog.dataset.crs, unscale=False)
        assert p[0] == 8917

    cutline = "POLYGON ((13 1685, 1010 6, 2650 967, 1630 2655, 13 1685))"
    with COGReader(COG_PATH, vrt_options={"cutline": cutline}) as cog:
        _, mask = cog.preview()
        assert not mask.all()
示例#15
0
async def cog_tilejson(
    request: Request,
    response: Response,
    TileMatrixSetId: TileMatrixSetNames = Query(
        TileMatrixSetNames.WebMercatorQuad,  # type: ignore
        description="TileMatrixSet Name (default: 'WebMercatorQuad')",
    ),
    url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
    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 TileJSON document for a COG."""
    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)

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

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

    response.headers["Cache-Control"] = "max-age=3600"
    return tjson
示例#16
0
def test_reader_tiles():
    """Test COGReader.tile."""
    lon = -58.181
    lat = 73.8794
    zoom = 7

    tms = morecantile.tms.get("WebMercatorQuad")
    x, y, z = tms.tile(lon, lat, zoom)

    with COGReader(COG_PATH, tms=tms) as cog:
        data, mask = cog.tile(x, y, z)
    assert data.shape == (1, 256, 256)
    assert mask.shape == (256, 256)

    with COGReader(COG_PATH, tms=tms) as cog:
        data, mask = cog.tile(x, y, z, indexes=(1, 1))
    assert data.shape == (2, 256, 256)
    assert mask.shape == (256, 256)

    with COGReader(COG_PATH, tms=tms) as cog:
        # indexes should be ignored, TODO: add warning
        data, mask = cog.tile(x, y, z, indexes=1, expression="B1+2,B1/3")
    assert data.shape == (2, 256, 256)
    assert mask.shape == (256, 256)

    crs = CRS.from_epsg(32621)
    extent = [166021.44, 0.00, 534994.66, 9329005.18]  # from http://epsg.io/32621
    tms = morecantile.TileMatrixSet.custom(extent, crs)
    x, y, z = tms.tile(lon, lat, zoom)

    with COGReader(COG_PATH, tms=tms) as cog:
        data, mask = cog.tile(x, y, z)
        assert data.shape == (1, 256, 256)
        assert mask.shape == (256, 256)
        x, y, z = tms.tile(lon + 10, lat, zoom)
        with pytest.raises(TileOutsideBounds):
            cog.tile(x, y, z)
示例#17
0
async def cog_metadata(
        resp: Response,
        url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
        metadata_params: CommonMetadataParams = Depends(),
):
    """Return the metadata of the COG."""
    with COGReader(url) as cog:
        info = cog.info
        stats = cog.stats(
            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,
        )
        info["statistics"] = stats

    resp.headers["Cache-Control"] = "max-age=3600"
    return info
示例#18
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])
示例#19
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}
示例#20
0
 def worker(asset: str) -> Dict:
     with COGReader(asset) as cog:
         return cog.metadata(*args, **kwargs)
示例#21
0
 def worker(asset: str) -> Dict:
     with COGReader(asset, tms=self.tms) as cog:
         return cog.info
示例#22
0
def mock_reader(src_path: str, *args, **kwargs) -> COGReader:
    """Mock rasterio.open."""
    assert src_path.startswith("https://myurl.com/")
    cog_path = os.path.basename(src_path)
    return COGReader(os.path.join(DATA_DIR, cog_path), *args, **kwargs)
示例#23
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,
    )
示例#24
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,
    )
示例#25
0
 def worker(asset: str) -> Dict:
     with COGReader(asset) as cog:
         return cog.stats(*args, **kwargs)
示例#26
0
def _read_tile(url: str, *args, **kwargs) -> Tuple[numpy.ndarray, numpy.ndarray]:
    """Read tile from an asset"""
    with COGReader(url) as cog:
        tile, mask = cog.tile(*args, **kwargs)
    return tile, mask
示例#27
0
def _read_point(asset: str, *args, **kwargs) -> List:
    """Read pixel value at a point from an asset"""
    with COGReader(asset) as cog:
        return cog.point(*args, **kwargs)
示例#28
0
def _tiler(asset):
    with COGReader(asset) as cog:
        cog.tile(*tile)
    return True
示例#29
0
def mock_reader(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/")
    cog_path = os.path.basename(src_path)
    return COGReader(os.path.join(prefix, cog_path), *args, **kwargs)
示例#30
0
 def worker(asset: str) -> List:
     with COGReader(asset) as cog:
         return cog.point(*args, **kwargs)