Esempio n. 1
0
def create_from_features(
    features,
    output,
    minzoom,
    maxzoom,
    property,
    quadkey_zoom,
    min_tile_cover,
    tile_cover_sort,
    name,
    description,
    attribution,
    quiet,
):
    """Create mosaic definition file."""
    mosaicjson = MosaicJSON.from_features(
        list(features),
        minzoom,
        maxzoom,
        quadkey_zoom=quadkey_zoom,
        accessor=lambda feature: feature["properties"][property],
        minimum_tile_cover=min_tile_cover,
        tile_cover_sort=tile_cover_sort,
        quiet=quiet,
    )

    if name:
        mosaicjson.name = name
    if description:
        mosaicjson.description = description
    if attribution:
        mosaicjson.attribution = attribution

    if output:
        with MosaicBackend(output, mosaic_def=mosaicjson) as mosaic:
            mosaic.write(overwrite=True)
    else:
        click.echo(mosaicjson.json(exclude_none=True))
Esempio n. 2
0
def tilejson(url: str,
             tile_format="png",
             tile_scale: int = 1,
             **kwargs: Any) -> Tuple[str, str, str]:
    """
    Handle /tilejson.json requests.

    Note: All the querystring parameters are translated to function keywords
    and passed as string value by lambda_proxy
    """
    if url is None:
        return ("NOK", "text/plain", "Missing 'URL' parameter")

    with MosaicBackend(url) as mosaic:
        mosaic_def = dict(mosaic.mosaic_def)

    return get_tilejson(mosaic_def,
                        url,
                        tile_scale,
                        tile_format,
                        host=app.host,
                        path="/tiles",
                        **kwargs)
Esempio n. 3
0
def _tilejson(
    url: str = None,
    tile_scale: int = 1,
    tile_format: str = None,
    **kwargs: Any,
) -> Tuple:
    """Handle /tilejson.json requests."""
    if not url:
        return ("NOK", "text/plain", "Missing URL parameter")

    kwargs.update(dict(url=url))
    host = app.host

    if tile_format in ["pbf", "mvt"]:
        tile_url = f"{host}/{{z}}/{{x}}/{{y}}.{tile_format}"
    elif tile_format in ["png", "jpg", "webp", "tif", "npy"]:
        tile_url = f"{host}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}"
    else:
        tile_url = f"{host}/{{z}}/{{x}}/{{y}}@{tile_scale}x"

    qs = urllib.parse.urlencode(list(kwargs.items()))
    if qs:
        tile_url += f"?{qs}"

    with MosaicBackend(url) as mosaic:
        meta = mosaic.metadata
        response = {
            "bounds": meta["bounds"],
            "center": meta["center"],
            "maxzoom": meta["maxzoom"],
            "minzoom": meta["minzoom"],
            "name": url,
            "tilejson": "2.1.0",
            "tiles": [tile_url],
        }
    return ("OK", "application/json",
            json.dumps(response, separators=(",", ":")))
Esempio n. 4
0
def _create(
    body: str,
    minzoom: Union[str, int] = None,
    maxzoom: Union[str, int] = None,
    min_tile_cover: Union[str, float] = None,
    tile_cover_sort: Union[str, bool] = False,
    tile_format: str = None,
    tile_scale: Union[str, int] = 1,
    **kwargs: Any,
) -> Tuple[str, str, str]:
    minzoom = int(minzoom) if isinstance(minzoom, str) else minzoom
    maxzoom = int(maxzoom) if isinstance(maxzoom, str) else maxzoom
    min_tile_cover = (
        float(min_tile_cover) if isinstance(min_tile_cover, str) else min_tile_cover
    )

    mosaicid = get_hash(
        body=body,
        minzoom=minzoom,
        maxzoom=maxzoom,
        min_tile_cover=min_tile_cover,
        tile_cover_sort=tile_cover_sort,
        version=mosaic_version,
    )
    try:
        with MosaicBackend(_create_mosaic_path(mosaicid), client=s3_client) as mosaic:
            meta = mosaic.metadata
    except:  # noqa
        body = json.loads(body)
        with rasterio.Env(aws_session):
            mosaic_definition = MosaicJSON.from_urls(
                body,
                minzoom=minzoom,
                maxzoom=maxzoom,
                minimum_tile_cover=min_tile_cover,
                tile_cover_sort=tile_cover_sort,
            )

        with MosaicBackend(
            _create_mosaic_path(mosaicid),
            mosaic_def=mosaic_definition,
            client=s3_client,
        ) as mosaic:
            mosaic.write()
            meta = mosaic.metadata

    if tile_format in ["pbf", "mvt"]:
        tile_url = f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}.{tile_format}"
    elif tile_format in ["png", "jpg", "webp", "tif", "npy"]:
        tile_url = (
            f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}"
        )
    else:
        tile_url = f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}@{tile_scale}x"

    qs = urllib.parse.urlencode(list(kwargs.items()))
    if qs:
        tile_url += f"?{qs}"

    meta = {
        "bounds": meta["bounds"],
        "center": meta["center"],
        "maxzoom": meta["maxzoom"],
        "minzoom": meta["minzoom"],
        "name": mosaicid,
        "tilejson": "2.1.0",
        "tiles": [tile_url],
    }

    return ("OK", "application/json", json.dumps(meta, separators=(",", ":")))
Esempio n. 5
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. 6
0
 def assets(qk: str = Query(...)):
     """Handle /assets requests."""
     x, y, z = mercantile.quadkey_to_tile(qk)
     with MosaicBackend(self.src_path) as mosaic:
         return mosaic.assets_for_tile(x, y, z)
Esempio n. 7
0
def upload(file, url):
    """Upload mosaic definition file."""
    mosaicjson = json.load(file)

    with MosaicBackend(url, mosaic_def=mosaicjson) as mosaic:
        mosaic.write(overwrite=True)
Esempio n. 8
0
def test_BaseReader():
    """Test BaseReader heritance methods."""
    assets = [asset1, asset2]
    mosaicdef = MosaicJSON.from_urls(assets, quiet=False)

    # add some offset to the center to make
    # sure BaseBackend forward center from the mosaic definition
    mosaicdef.center = [x + 1 for x in mosaicdef.center]

    with MosaicBackend(None, mosaic_def=mosaicdef) as mosaic:
        (t, _), assets_used = mosaic.tile(150, 182, 9)
        assert t.shape

        (tR, _), assets_usedR = mosaic.tile(150, 182, 9, reverse=True)
        assert tR.shape
        assert not numpy.array_equal(t, tR)

        assert assets_used[0] == assets_usedR[-1]

        with pytest.raises(NoAssetFoundError):
            mosaic.tile(200, 182, 9)

        pts = mosaic.point(-73, 45)
        assert len(pts) == 2
        assert pts[0]["asset"]
        assert pts[1]["values"]

        ptsR = mosaic.point(-73, 45, reverse=True)
        assert len(ptsR) == 2
        assert ptsR[0]["asset"] == pts[-1]["asset"]

        pts = mosaic.point(-72.5, 46)
        assert len(pts) == 1

        with pytest.raises(NoAssetFoundError):
            mosaic.point(-60, 45)

        assert mosaic.minzoom
        assert mosaic.maxzoom
        assert mosaic.bounds
        assert mosaic.center == mosaicdef.center

        with pytest.raises(NotImplementedError):
            mosaic.stats()

        with pytest.raises(NotImplementedError):
            mosaic.preview()

        with pytest.raises(NotImplementedError):
            mosaic.part()

        info = mosaic.info()
        assert list(info.dict()) == [
            "bounds",
            "center",
            "minzoom",
            "maxzoom",
            "name",
            "quadkeys",
        ]

        assert mosaic.spatial_info
Esempio n. 9
0
def test_file_backend():
    """Test File backend."""
    with MosaicBackend(mosaic_gz) as mosaic:
        assert mosaic._backend_name == "File"
        assert isinstance(mosaic, FileBackend)
        assert (mosaic.mosaicid ==
                "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
        assert mosaic.quadkey_zoom == 7

        info = mosaic.info()
        assert not info["quadkeys"]
        assert list(info.dict()) == [
            "bounds",
            "center",
            "minzoom",
            "maxzoom",
            "name",
            "quadkeys",
        ]

        info = mosaic.info(quadkeys=True)
        assert info["quadkeys"]

        assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "quadkey_zoom",
            "bounds",
            "center",
        ]
        assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"]

        assert len(mosaic.get_assets(150, 182, 9)) == 2
        assert len(mosaic.get_assets(147, 182, 12)) == 0

    with MosaicBackend(mosaic_bin, backend_options={"gzip": True}) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert (mosaic.mosaicid ==
                "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
        assert mosaic.quadkey_zoom == 7

    with MosaicBackend(mosaic_json) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7

    with MosaicBackend(mosaic_jsonV1) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7
        assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "bounds",
            "center",
        ]

    with pytest.raises(ValidationError):
        with MosaicBackend("afile.json", mosaic_def={}):
            pass

    runner = CliRunner()
    with runner.isolated_filesystem():
        with MosaicBackend("mosaic.json", mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            with open("mosaic.json") as f:
                m = json.loads(f.read())
                assert m["quadkey_zoom"] == 7
            with pytest.raises(MosaicExistsError):
                mosaic.write()
            mosaic.write(overwrite=True)

        with MosaicBackend("mosaic.json.gz",
                           mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            with open("mosaic.json.gz", "rb") as f:
                m = json.loads(_decompress_gz(f.read()))
                assert m["quadkey_zoom"] == 7

        with MosaicBackend("abinaryfile.bin",
                           mosaic_def=mosaic_content) as mosaic:
            mosaic.write(gzip=True)
            with open("abinaryfile.bin", "rb") as f:
                m = json.loads(_decompress_gz(f.read()))
                assert m["quadkey_zoom"] == 7
Esempio n. 10
0
def test_file_backend():
    """Test File backend."""
    with MosaicBackend(mosaic_gz) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert (mosaic.mosaicid ==
                "f39f05644731addf1d183fa094ff6478900a27912ad035ef570231b1")
        assert mosaic.quadkey_zoom == 7
        assert list(mosaic.metadata.keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "quadkey_zoom",
            "bounds",
            "center",
        ]
        assert mosaic.tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.point(-73, 45) == ["cog1.tif", "cog2.tif"]

    with MosaicBackend(mosaic_bin, gzip=True) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert (mosaic.mosaicid ==
                "f39f05644731addf1d183fa094ff6478900a27912ad035ef570231b1")
        assert mosaic.quadkey_zoom == 7

    with MosaicBackend(mosaic_json) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7

    with MosaicBackend(mosaic_jsonV1) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7
        assert list(mosaic.metadata.keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "bounds",
            "center",
        ]

    runner = CliRunner()
    with runner.isolated_filesystem():
        with MosaicBackend("mosaic.json", mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            with open("mosaic.json") as f:
                m = json.loads(f.read())
                print
                assert m["quadkey_zoom"] == 7

        with MosaicBackend("mosaic.json.gz",
                           mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            with open("mosaic.json.gz", "rb") as f:
                m = json.loads(_decompress_gz(f.read()))
                assert m["quadkey_zoom"] == 7

        with MosaicBackend("abinaryfile.bin",
                           mosaic_def=mosaic_content) as mosaic:
            mosaic.write(gzip=True)
            with open("abinaryfile.bin", "rb") as f:
                m = json.loads(_decompress_gz(f.read()))
                assert m["quadkey_zoom"] == 7
Esempio n. 11
0
def process(message):
    """Create NPY tile."""
    out_bucket = os.environ["OUTPUT_BUCKET"]

    # Parse SNS message
    if isinstance(message, str):
        message = json.loads(message)
    message = Message(**message)

    # Import Reader Class
    module, classname = message.reader.rsplit(".", 1)
    reader = getattr(importlib.import_module(module), classname)  # noqa
    if not issubclass(reader, BaseReader):
        warnings.warn("Reader should be a subclass of rio_tiler.io.BaseReader")

    kwargs: Dict[str, Any] = {}
    if message.expression:
        kwargs["expression"] = message.expression
    if message.pixel_selection:
        kwargs["pixel_selection"] = message.pixel_selection.method

    # We allow multiple datasets in form of `dataset1,dataset2,dataset3`
    for dataset in message.dataset.split(","):
        # MosaicReader
        # mosaic+mosaicid://
        # mosaic+https://
        # mosaic+s3://
        parsed = urlparse(dataset)
        if parsed.scheme and (parsed.scheme.startswith("mosaic+")
                              or parsed.scheme == "mosaicid"):
            mosaic_dataset = dataset.replace("mosaic+", "")

            if mosaic_dataset.startswith(
                    "mosaicid://"):  # dataset is a mosaic id
                bname = mosaic_dataset.replace("mosaicid://", "")
                if mosaic_config.backend == "dynamodb://":
                    url = f"{mosaic_config.backend}{mosaic_config.host}:{bname}"
                else:
                    url = f"{mosaic_config.backend}{mosaic_config.host}/{bname}{mosaic_config.format}"

            else:  # dataset is a full mosaic path
                url = mosaic_dataset
                bname = os.path.basename(mosaic_dataset).split(".")[0]

            out_key = os.path.join(
                bname,
                f"{message.tile.z}-{message.tile.x}-{message.tile.y}.npz")
            with MosaicBackend(url, reader=reader) as src_dst:
                if not message.expression:
                    # For Mosaic we cannot guess the assets or bands
                    # User will have to pass indexes=B1,B2,B3 or indexes=asset1,asset2
                    bidx_kwargs = _get_options(src_dst, message.indexes)
                    kwargs = {**kwargs, **bidx_kwargs}

                threads = int(os.getenv("MOSAIC_CONCURRENCY", MAX_THREADS))
                try:
                    data, _ = src_dst.tile(*message.tile,
                                           threads=threads,
                                           **kwargs)
                except (NoAssetFoundError, EmptyMosaicError):
                    logger.warning(
                        f"No data of {mosaic_dataset} - {message.tile.z}-{message.tile.x}-{message.tile.y}"
                    )
                    continue

        # BaseReader
        else:
            bname = os.path.basename(dataset).split(".")[0]
            out_key = os.path.join(
                bname,
                f"{message.tile.z}-{message.tile.x}-{message.tile.y}.npz")
            with reader(dataset) as src_dst:
                if not message.expression:
                    bidx_kwargs = _get_options(src_dst, message.indexes)
                    kwargs = {**kwargs, **bidx_kwargs}
                try:
                    data = src_dst.tile(*message.tile, **kwargs)
                except TileOutsideBounds:
                    continue

        bio = BytesIO()
        numpy.savez_compressed(bio, data=data.data, mask=data.mask)
        bio.seek(0)
        _s3_upload(bio, out_bucket, out_key)

    return True
Esempio n. 12
0
def test_file_backend():
    """Test File backend."""
    with MosaicBackend(mosaic_gz) as mosaic:
        assert mosaic._backend_name == "File"
        assert isinstance(mosaic, FileBackend)
        assert (mosaic.mosaicid ==
                "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
        assert mosaic.quadkey_zoom == 7
        assert mosaic.minzoom == mosaic.mosaic_def.minzoom

        info = mosaic.info()
        assert not info["quadkeys"]
        assert list(info.dict()) == [
            "bounds",
            "center",
            "minzoom",
            "maxzoom",
            "name",
            "quadkeys",
        ]

        info = mosaic.info(quadkeys=True)
        assert info["quadkeys"]

        assert list(
            mosaic.mosaic_def.dict(exclude_none=True,
                                   exclude={"tiles"}).keys()) == [
                                       "mosaicjson",
                                       "version",
                                       "minzoom",
                                       "maxzoom",
                                       "quadkey_zoom",
                                       "bounds",
                                       "center",
                                   ]
        # make sure we do not return asset twice (e.g for parent tile)
        assert mosaic.assets_for_tile(18, 22, 6) == ["cog1.tif", "cog2.tif"]

        assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"]

        assert len(mosaic.get_assets(150, 182, 9)) == 2
        assert len(mosaic.get_assets(147, 182, 12)) == 0

        assert mosaic.assets_for_bbox(
            -74.53125, 45.583289756006316, -73.828125,
            46.07323062540836) == ["cog1.tif", "cog2.tif"]

    with MosaicBackend(mosaic_json) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7

    with MosaicBackend(mosaic_jsonV1) as mosaic:
        assert isinstance(mosaic, FileBackend)
        assert mosaic.quadkey_zoom == 7
        assert list(
            mosaic.mosaic_def.dict(exclude_none=True,
                                   exclude={"tiles"}).keys()) == [
                                       "mosaicjson", "version", "minzoom",
                                       "maxzoom", "bounds", "center"
                                   ]

    with pytest.raises(ValidationError):
        with MosaicBackend("afile.json", mosaic_def={}):
            pass

    runner = CliRunner()
    with runner.isolated_filesystem():
        with MosaicBackend("mosaic.json", mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            assert mosaic.minzoom == mosaic_content["minzoom"]

            with open("mosaic.json") as f:
                m = json.loads(f.read())
                assert m["quadkey_zoom"] == 7
            with pytest.raises(MosaicExistsError):
                mosaic.write()
            mosaic.write(overwrite=True)

        with MosaicBackend("mosaic.json.gz",
                           mosaic_def=mosaic_content) as mosaic:
            mosaic.write()
            with open("mosaic.json.gz", "rb") as f:
                m = json.loads(_decompress_gz(f.read()))
                assert m["quadkey_zoom"] == 7

        mosaic_oneasset = MosaicJSON.from_urls([asset1], quiet=True)

        with MosaicBackend("umosaic.json.gz",
                           mosaic_def=mosaic_oneasset) as mosaic:
            mosaic.write()
            assert len(mosaic.get_assets(150, 182, 9)) == 1

        with MosaicBackend("umosaic.json.gz") as mosaic:
            features = get_footprints([asset2], quiet=True)
            mosaic.update(features)
            assets = mosaic.get_assets(150, 182, 9)
            assert len(assets) == 2
            assert assets[0] == asset2
            assert assets[1] == asset1
Esempio n. 13
0
def test_gs_backend(session):
    """Test GS backend."""
    with open(mosaic_gz, "rb") as f:
        session.return_value.bucket.return_value.blob.return_value.download_as_bytes.return_value = (
            f.read())

    session.return_value.bucket.return_value.blob.return_value.upload_from_string.return_value = (
        True)
    session.return_value.bucket.return_value.blob.return_value.exists.return_value = (
        False)

    with MosaicBackend("gs://mybucket/mymosaic.json.gz") as mosaic:
        assert mosaic._backend_name == "Google Cloud Storage"
        assert isinstance(mosaic, GCSBackend)
        assert (mosaic.mosaicid ==
                "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
        assert mosaic.quadkey_zoom == 7
        assert list(
            mosaic.mosaic_def.dict(exclude_none=True,
                                   exclude={"tiles"}).keys()) == [
                                       "mosaicjson",
                                       "version",
                                       "minzoom",
                                       "maxzoom",
                                       "quadkey_zoom",
                                       "bounds",
                                       "center",
                                   ]
        assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"]
        session.return_value.bucket.assert_called_once_with("mybucket")
        session.return_value.bucket.return_value.blob.assert_called_once_with(
            "mymosaic.json.gz")

        session.return_value.bucket.return_value.blob.return_value.upload_from_string.assert_not_called(
        )
        session.return_value.bucket.return_value.blob.return_value.exists.assert_not_called(
        )
        session.reset_mock()

    with open(mosaic_gz, "rb") as f:
        session.return_value.bucket.return_value.blob.return_value.download_as_bytes.return_value = (
            f.read())

    session.return_value.bucket.return_value.blob.return_value.upload_from_string.return_value = (
        True)
    session.return_value.bucket.return_value.blob.return_value.exists.return_value = (
        False)

    with MosaicBackend("gs://mybucket/mymosaic.json.gz",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, GCSBackend)
        mosaic.write()
    session.return_value.bucket.return_value.blob.return_value.download_as_bytes.assert_not_called(
    )

    session.return_value.bucket.return_value.blob.return_value.exists.assert_called_once(
    )
    session.reset_mock()

    session.return_value.bucket.return_value.blob.return_value.exists.return_value = (
        False)
    with MosaicBackend("gs://mybucket/00000.json",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, GCSBackend)
        mosaic.write()
    session.return_value.bucket.return_value.blob.return_value.download_as_bytes.assert_not_called(
    )
    session.return_value.bucket.return_value.blob.return_value.exists.assert_called_once(
    )
    session.reset_mock()

    session.return_value.bucket.return_value.blob.return_value.exists.return_value = (
        True)
    with MosaicBackend("gs://mybucket/00000.json",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, GCSBackend)
        with pytest.raises(MosaicExistsError):
            mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.bucket.return_value.blob.return_value.exists.assert_called_once(
    )
    session.reset_mock()

    session.return_value.bucket.return_value.blob.return_value.exists.return_value = (
        True)
    with MosaicBackend("gs://mybucket/00000.json",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, GCSBackend)
        mosaic.write(overwrite=True)
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_not_called()
    session.reset_mock()
Esempio n. 14
0
def tiles(
    url: 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,
    pan: bool = False,
    pixel_selection: str = "first",
) -> Tuple[str, str, BinaryIO]:
    """Handle tile requests."""
    with MosaicBackend(url) 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

    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,
            pan=pan,
        )

    elif bands is not None:
        tile, mask = mosaic_tiler(
            assets,
            x,
            y,
            z,
            landsatTiler,
            pixel_selection=pixel_selection(),
            bands=tuple(bands.split(",")),
            tilesize=tilesize,
            pan=pan,
        )
    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")

    assets_str = json.dumps(assets, separators=(",", ":"))
    return_kwargs = {"custom_headers": {"X-ASSETS": assets_str}}

    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(
                        render(
                            img,
                            mask[i],
                            img_format="png",
                            colormap=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(), return_kwargs)

    rtile = post_process_tile(tile,
                              mask,
                              rescale=rescale,
                              color_formula=color_ops)

    if ext == "bin":
        # Flatten in Row-major order
        buf = rtile.tobytes(order='C')
        return ("OK", "application/x-binary", buf, return_kwargs)

    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}",
        render(rtile, mask, img_format=driver, colormap=color_map, **options),
        return_kwargs,
    )
Esempio n. 15
0
def create_low_level_cogs(
    mosaic_path: str,
    output_profile: Dict,
    prefix: str = "mosaic_ovr",
    max_overview_level: int = 6,
    config: Dict = None,
    threads=1,
) -> None:
    """
    Create WebOptimized Overview COG from a mosaic definition file.

    Attributes
    ----------
    mosaic_path : str, required
        Mosaic definition path.
    output_profile : dict, required
    prefix : str
    max_overview_level : int
    config : dict
        Rasterio Env options.

    """
    with MosaicBackend(mosaic_path) as mosaic:
        base_zoom = mosaic.metadata["minzoom"] - 1
        mosaic_quadkey_zoom = mosaic.quadkey_zoom
        bounds = mosaic.metadata["bounds"]
        mosaic_quadkeys = set(mosaic._quadkeys)

        # Select a random quakey/asset and get dataset info
        tile = mercantile.quadkey_to_tile(random.sample(mosaic_quadkeys, 1)[0])
        assets = mosaic.tile(*tile)
        info = _get_info(assets[0])

        extrema = tile_extrema(bounds, base_zoom)
        tilesize = 256
        res = _meters_per_pixel(base_zoom, 0, tilesize=tilesize)

        # Create multiples files if coverage is too big
        extremas = _split_extrema(extrema,
                                  max_ovr=max_overview_level,
                                  tilesize=tilesize)
        for ix, extrema in enumerate(extremas):
            blocks = list(_get_blocks(extrema))
            random.shuffle(blocks)

            width = (extrema["x"]["max"] - extrema["x"]["min"]) * tilesize
            height = (extrema["y"]["max"] - extrema["y"]["min"]) * tilesize
            w, n = mercantile.xy(*mercantile.ul(
                extrema["x"]["min"], extrema["y"]["min"], base_zoom))

            params = dict(
                driver="GTiff",
                dtype=info[1],
                count=len(info[6]),
                width=width,
                height=height,
                crs="epsg:3857",
                transform=Affine(res, 0, w, 0, -res, n),
                nodata=info[4],
                tiled=True,
                blockxsize=256,
                blockysize=256,
            )

            config = config or {}
            with rasterio.Env(**config):
                with MemoryFile() as memfile:
                    with memfile.open(**params) as mem:

                        def _get_tile(wind):
                            idx, window = wind
                            x = extrema["x"]["min"] + idx[1]
                            y = extrema["y"]["min"] + idx[0]
                            t = mercantile.Tile(x, y, base_zoom)

                            kds = set(find_quadkeys(t, mosaic_quadkey_zoom))
                            if not mosaic_quadkeys.intersection(kds):
                                return window, None, None

                            assets = mosaic.tile(*t)
                            if not assets:
                                raise Exception(
                                    f"No asset for tile {x}-{y}-{base_zoom}")

                            if assets:
                                tile, mask = mosaic_tiler(
                                    assets,
                                    x,
                                    y,
                                    base_zoom,
                                    cogeo.tile,
                                    indexes=info[6],
                                    tilesize=tilesize,
                                    pixel_selection=defaults.FirstMethod(),
                                )

                            return window, tile, mask

                        with futures.ThreadPoolExecutor(
                                max_workers=threads) as executor:
                            future_work = [
                                executor.submit(_get_tile, item)
                                for item in blocks
                            ]
                            with click.progressbar(
                                    futures.as_completed(future_work),
                                    length=len(future_work),
                                    show_percent=True,
                            ) as future:
                                for res in future:
                                    pass

                        for f in _filter_futures(future_work):
                            window, tile, mask = f
                            if tile is None:
                                continue

                            mem.write(tile, window=window)
                            if info[5]:
                                mem.write_mask(mask.astype("uint8"),
                                               window=window)

                        cog_translate(
                            mem,
                            f"{prefix}_{ix}.tif",
                            output_profile,
                            config=config,
                            in_memory=True,
                        )
Esempio n. 16
0
def create_overview_cogs(
    mosaic_path: str,
    output_profile: Dict,
    prefix: str = "mosaic_ovr",
    max_overview_level: int = 6,
    method: str = "first",
    config: Dict = None,
    threads=1,
    in_memory: bool = True,
) -> None:
    """
    Create Low resolution mosaic image from a mosaicJSON.

    The output will be a web optimized COG with bounds matching the mosaicJSON bounds and
    with its resolution matching the mosaic MinZoom - 1.

    Attributes
    ----------
    mosaic_path : str, required
        Mosaic definition path.
    output_profile : dict, required
    prefix : str
    max_overview_level : int
    method: str, optional
        pixel_selection method name (default is 'first').
    config : dict
        Rasterio Env options.
    threads: int, optional
        maximum number of threads to use (default is 1).
    in_memory: bool, optional
        Force COG creation in memory (default is True).

    """
    pixel_method = PIXSEL_METHODS[method]

    with MosaicBackend(mosaic_path) as mosaic:
        base_zoom = mosaic.metadata["minzoom"] - 1
        mosaic_quadkey_zoom = mosaic.quadkey_zoom
        bounds = mosaic.metadata["bounds"]
        mosaic_quadkeys = set(mosaic._quadkeys)

        # Select a random quakey/asset and get dataset info
        tile = mercantile.quadkey_to_tile(random.sample(mosaic_quadkeys, 1)[0])
        assets = mosaic.assets_for_tile(*tile)
        info = _get_info(assets[0])

        extrema = tile_extrema(bounds, base_zoom)
        tilesize = 256
        resolution = _meters_per_pixel(base_zoom, 0, tilesize=tilesize)

        # Create multiples files if coverage is too big
        extremas = _split_extrema(extrema, max_ovr=max_overview_level)
        for ix, extrema in enumerate(extremas):
            click.echo(f"Part {1 + ix}/{len(extremas)}", err=True)
            output_path = f"{prefix}_{ix}.tif"

            blocks = list(_get_blocks(extrema, tilesize))
            random.shuffle(blocks)

            width = (extrema["x"]["max"] - extrema["x"]["min"]) * tilesize
            height = (extrema["y"]["max"] - extrema["y"]["min"]) * tilesize
            w, n = mercantile.xy(*mercantile.ul(
                extrema["x"]["min"], extrema["y"]["min"], base_zoom))

            params = dict(
                driver="GTiff",
                dtype=info["dtype"],
                count=len(info["band_descriptions"]),
                width=width,
                height=height,
                crs="epsg:3857",
                transform=Affine(resolution, 0, w, 0, -resolution, n),
                nodata=info["nodata_value"],
            )
            params.update(**output_profile)

            config = config or {}
            with rasterio.Env(**config):
                with ExitStack() as ctx:
                    if in_memory:
                        tmpfile = ctx.enter_context(MemoryFile())
                        tmp_dst = ctx.enter_context(tmpfile.open(**params))
                    else:
                        tmpfile = ctx.enter_context(
                            TemporaryRasterFile(output_path))
                        tmp_dst = ctx.enter_context(
                            rasterio.open(tmpfile.name, "w", **params))

                    def _get_tile(wind):
                        idx, window = wind
                        x = extrema["x"]["min"] + idx[1]
                        y = extrema["y"]["min"] + idx[0]
                        t = mercantile.Tile(x, y, base_zoom)

                        kds = set(find_quadkeys(t, mosaic_quadkey_zoom))
                        if not mosaic_quadkeys.intersection(kds):
                            return window, None, None

                        try:
                            (tile, mask), _ = mosaic.tile(
                                t.x,
                                t.y,
                                t.z,
                                tilesize=tilesize,
                                pixel_selection=pixel_method(),
                            )
                        except NoAssetFoundError:
                            return window, None, None

                        return window, tile, mask

                    with futures.ThreadPoolExecutor(
                            max_workers=threads) as executor:
                        future_work = [
                            executor.submit(_get_tile, item) for item in blocks
                        ]
                        with click.progressbar(
                                futures.as_completed(future_work),
                                length=len(future_work),
                                show_percent=True,
                                label="Loading tiles",
                        ) as future:
                            for res in future:
                                pass

                    for f in _filter_futures(future_work):
                        window, tile, mask = f
                        if tile is None:
                            continue

                        tmp_dst.write(tile, window=window)
                        if info["nodata_type"] == "Mask":
                            tmp_dst.write_mask(mask.astype("uint8"),
                                               window=window)

                    min_tile_size = tilesize = min(
                        int(output_profile["blockxsize"]),
                        int(output_profile["blockysize"]),
                    )
                    overview_level = get_maximum_overview_level(
                        tmp_dst.width, tmp_dst.height, minsize=min_tile_size)
                    overviews = [2**j for j in range(1, overview_level + 1)]
                    tmp_dst.build_overviews(overviews)
                    copy(tmp_dst,
                         output_path,
                         copy_src_overviews=True,
                         **params)
Esempio n. 17
0
def test_s3_backend(session):
    """Test S3 backend."""
    with open(mosaic_gz, "rb") as f:
        session.return_value.client.return_value.get_object.return_value = {
            "Body": BytesIO(f.read())
        }
    session.return_value.client.return_value.put_object.return_value = True

    with MosaicBackend("s3://mybucket/mymosaic.json.gz") as mosaic:
        assert isinstance(mosaic, S3Backend)
        assert (mosaic.mosaicid ==
                "f39f05644731addf1d183fa094ff6478900a27912ad035ef570231b1")
        assert mosaic.quadkey_zoom == 7
        assert list(mosaic.metadata.keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "quadkey_zoom",
            "bounds",
            "center",
        ]
        assert mosaic.tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.point(-73, 45) == ["cog1.tif", "cog2.tif"]
        session.return_value.client.return_value.get_object.assert_called_once_with(
            Bucket="mybucket", Key="mymosaic.json.gz")
        session.return_value.client.return_value.put_object.assert_not_called()
        session.reset_mock()

    with open(mosaic_gz, "rb") as f:
        session.return_value.client.return_value.get_object.return_value = {
            "Body": BytesIO(f.read())
        }
    session.return_value.client.return_value.put_object.return_value = True

    with MosaicBackend("s3://mybucket/mymosaic.json.gz",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "mymosaic.json.gz"
    assert MosaicJSON(**json.loads(_decompress_gz(kwargs["Body"])))
    session.reset_mock()

    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write(gzip=True)
    session.return_value.client.return_value.get_object.assert_not_called()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "00000"
    assert MosaicJSON(**json.loads(_decompress_gz(kwargs["Body"])))
    session.reset_mock()

    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "00000"
    assert MosaicJSON(**json.loads(kwargs["Body"]))
    session.reset_mock()
Esempio n. 18
0
def mosaicjson_bounds(mosaic_path: str = Depends(MosaicPath)):
    """Read MosaicJSON bounds"""
    with MosaicBackend(mosaic_path) as mosaic:
        return {"bounds": mosaic.mosaic_def.bounds}
Esempio n. 19
0
def test_s3_backend(session):
    """Test S3 backend."""
    with open(mosaic_gz, "rb") as f:
        session.return_value.client.return_value.get_object.return_value = {
            "Body": BytesIO(f.read())
        }
    session.return_value.client.return_value.put_object.return_value = True
    session.return_value.client.return_value.head_object.return_value = False

    with MosaicBackend("s3://mybucket/mymosaic.json.gz") as mosaic:
        assert mosaic._backend_name == "AWS S3"
        assert isinstance(mosaic, S3Backend)
        assert (mosaic.mosaicid ==
                "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
        assert mosaic.quadkey_zoom == 7
        assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [
            "mosaicjson",
            "version",
            "minzoom",
            "maxzoom",
            "quadkey_zoom",
            "bounds",
            "center",
        ]
        assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"]
        session.return_value.client.return_value.get_object.assert_called_once_with(
            Bucket="mybucket", Key="mymosaic.json.gz")
        session.return_value.client.return_value.put_object.assert_not_called()
        session.return_value.client.return_value.head_object.assert_not_called(
        )
        session.reset_mock()

    with open(mosaic_gz, "rb") as f:
        session.return_value.client.return_value.get_object.return_value = {
            "Body": BytesIO(f.read())
        }
    session.return_value.client.return_value.put_object.return_value = True
    session.return_value.client.return_value.head_object.return_value = False

    with MosaicBackend("s3://mybucket/mymosaic.json.gz",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_called_once()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "mymosaic.json.gz"
    assert MosaicJSON(**json.loads(_decompress_gz(kwargs["Body"])))
    session.reset_mock()

    session.return_value.client.return_value.head_object.return_value = False
    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write(gzip=True)
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_called_once()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "00000"
    assert MosaicJSON(**json.loads(_decompress_gz(kwargs["Body"])))
    session.reset_mock()

    session.return_value.client.return_value.head_object.return_value = False
    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_called_once()
    kwargs = session.return_value.client.return_value.put_object.call_args[1]
    assert kwargs["Bucket"] == "mybucket"
    assert kwargs["Key"] == "00000"
    assert MosaicJSON(**json.loads(kwargs["Body"]))
    session.reset_mock()

    session.return_value.client.return_value.head_object.return_value = True
    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        with pytest.raises(MosaicExistsError):
            mosaic.write()
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_called_once()
    kwargs = session.return_value.client.return_value.put_object.assert_not_called(
    )
    session.reset_mock()

    session.return_value.client.return_value.head_object.return_value = True
    with MosaicBackend("s3://mybucket/00000",
                       mosaic_def=mosaic_content) as mosaic:
        assert isinstance(mosaic, S3Backend)
        mosaic.write(overwrite=True)
    session.return_value.client.return_value.get_object.assert_not_called()
    session.return_value.client.return_value.head_object.assert_not_called()
    kwargs = session.return_value.client.return_value.put_object.assert_called_once(
    )
    session.reset_mock()
Esempio n. 20
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
    )
Esempio n. 21
0
def test_mosaic_crud_error(mosaic_path):
    with pytest.raises(MosaicError):
        with MosaicBackend(mosaic_path):
            ...
Esempio n. 22
0
def read_mosaicjson(mosaic_path: str = Depends(MosaicPath)):
    """Read a MosaicJSON"""
    with MosaicBackend(mosaic_path) as mosaic:
        return mosaic.mosaic_def
Esempio n. 23
0
def test_sqlite_backend():
    """Test sqlite backend."""
    with MosaicBackend(f"sqlite:///{mosaic_db}:test") as mosaic:
        assert mosaic._backend_name == "SQLite"
        assert isinstance(mosaic, SQLiteBackend)
        assert (mosaic.mosaicid ==
                "f7fc24d47a79f1496dcdf9997de83e6305c252a931fba2c7d006b7d8")
        assert mosaic.quadkey_zoom == 7

        info = mosaic.info()
        assert not info["quadkeys"]
        assert list(info.dict()) == [
            "bounds",
            "center",
            "minzoom",
            "maxzoom",
            "name",
            "quadkeys",
        ]

        info = mosaic.info(quadkeys=True)
        assert info["quadkeys"]

        assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [
            "mosaicjson",
            "name",
            "version",
            "minzoom",
            "maxzoom",
            "quadkey_zoom",
            "bounds",
            "center",
        ]
        assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"]
        assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"]

        assert len(mosaic.get_assets(150, 182, 9)) == 2
        assert len(mosaic.get_assets(147, 182, 12)) == 0

    # Validation error, mosaic_def is empty
    with pytest.raises(ValidationError):
        with MosaicBackend("sqlite:///:memory::test", mosaic_def={}):
            pass

    # invalid scheme `sqlit://`
    with pytest.raises(ValueError):
        with SQLiteBackend("sqlit:///:memory::test"):
            pass

    # `:` is an invalid character for mosaic name
    with pytest.raises(ValueError):
        with SQLiteBackend("sqlite:///:memory::test:"):
            pass

    # `mosaicjson_metadata` is a reserved mosaic name
    with pytest.raises(AssertionError):
        with MosaicBackend("sqlite:///:memory::mosaicjson_metadata"):
            pass

    # Warns when changing name
    with pytest.warns(UserWarning):
        with MosaicBackend(mosaic_gz) as m:
            with MosaicBackend("sqlite:///:memory::test",
                               mosaic_def=m.mosaic_def) as d:
                assert d.mosaic_def.name == "test"

    # need to set overwrite when mosaic already exists
    with MosaicBackend("sqlite:///:memory::test",
                       mosaic_def=mosaic_content) as mosaic:
        mosaic.write()
        with pytest.raises(MosaicExistsError):
            mosaic.write()
        mosaic.write(overwrite=True)

    # files doesn't exists
    with pytest.raises(MosaicNotFoundError):
        with MosaicBackend("sqlite:///test.db:test2") as mosaic:
            pass

    # mosaic doesn't exists in DB
    with pytest.raises(MosaicNotFoundError):
        with MosaicBackend(f"sqlite:///{mosaic_db}:test2") as mosaic:
            pass

    # Test with `.` in mosaic name
    with pytest.warns(UserWarning):
        with MosaicBackend("sqlite:///:memory::test.mosaic",
                           mosaic_def=mosaic_content) as m:
            m.write()
            assert m._mosaic_exists()
            assert m.mosaic_def.name == "test.mosaic"

            m.delete()
            assert not m._mosaic_exists()
            assert not m._fetch_metadata()

    mosaic_oneasset = MosaicJSON.from_urls([asset1], quiet=True)
    features = get_footprints([asset2], quiet=True)

    # Test update methods
    with MosaicBackend("sqlite:///:memory::test",
                       mosaic_def=mosaic_oneasset) as m:
        m.write()
        meta = m.metadata
        assert len(m.get_assets(150, 182, 9)) == 1

        m.update(features)
        assert not m.metadata == meta

        assets = m.get_assets(150, 182, 9)
        assert len(assets) == 2
        assert assets[0] == asset2
        assert assets[1] == asset1

    # Test update with `add_first=False`
    with MosaicBackend("sqlite:///:memory::test2",
                       mosaic_def=mosaic_oneasset) as m:
        m.write()
        meta = m.metadata
        assert len(m.get_assets(150, 182, 9)) == 1

        m.update(features, add_first=False)
        assert not m.metadata == meta

        assets = m.get_assets(150, 182, 9)
        assert len(assets) == 2
        assert assets[0] == asset1
        assert assets[1] == asset2

    assert SQLiteBackend.list_mosaics_in_db(mosaic_db) == ["test"]
    assert SQLiteBackend.list_mosaics_in_db(f"sqlite:///{mosaic_db}") == [
        "test"
    ]

    with pytest.raises(ValueError):
        assert SQLiteBackend.list_mosaics_in_db("test.db")
Esempio n. 24
0
def test_InMemoryReader():
    """Test MemoryBackend."""
    assets = [asset1, asset2]
    mosaicdef = MosaicJSON.from_urls(assets, quiet=False)

    with MosaicBackend(":memory:", mosaic_def=mosaicdef) as mosaic:
        assert isinstance(mosaic, MemoryBackend)
        assert mosaic.path == ":memory:"
        mosaic.write()
        mosaic._read()

    with MosaicBackend(None, mosaic_def=mosaicdef) as mosaic:
        assert isinstance(mosaic, MemoryBackend)
        assert mosaic.path == ":memory:"

    with MemoryBackend(mosaic_def=mosaicdef) as mosaic:
        (t, _), assets_used = mosaic.tile(150, 182, 9)
        assert t.shape

        (tR, _), assets_usedR = mosaic.tile(150, 182, 9, reverse=True)
        assert tR.shape
        assert not numpy.array_equal(t, tR)

        assert assets_used[0] == assets_usedR[-1]

        with pytest.raises(NoAssetFoundError):
            mosaic.tile(200, 182, 9)

        pts = mosaic.point(-73, 45)
        assert len(pts) == 2
        assert pts[0][0].endswith(".tif")
        assert len(pts[0][1]) == 3

        ptsR = mosaic.point(-73, 45, reverse=True)
        assert len(ptsR) == 2
        assert ptsR[0][0] == pts[-1][0]

        pts = mosaic.point(-72.5, 46)
        assert len(pts) == 1

        with pytest.raises(PointOutsideBounds):
            mosaic.point(-72.5, 46, allowed_exceptions=None)

        with pytest.raises(NoAssetFoundError):
            mosaic.point(-60, 45)

        assert mosaic.minzoom
        assert mosaic.maxzoom
        assert mosaic.bounds
        assert mosaic.center == mosaicdef.center

        with pytest.raises(NotImplementedError):
            mosaic.stats()

        with pytest.raises(NotImplementedError):
            mosaic.preview()

        with pytest.raises(NotImplementedError):
            mosaic.part()

        with pytest.raises(NotImplementedError):
            mosaic.feature()

        info = mosaic.info()
        assert list(info.dict()) == [
            "bounds",
            "center",
            "minzoom",
            "maxzoom",
            "name",
            "quadkeys",
        ]

        assert mosaic.spatial_info

    mosaic_oneasset = MosaicJSON.from_urls([asset1], quiet=True)
    with MemoryBackend(mosaic_def=mosaic_oneasset) as mosaic:
        assert isinstance(mosaic, MemoryBackend)
        assert len(mosaic.get_assets(150, 182, 9)) == 1
        features = get_footprints([asset2], quiet=True)
        mosaic.update(features)
        assets = mosaic.get_assets(150, 182, 9)
        assert len(assets) == 2
        assert assets[0] == asset2
        assert assets[1] == asset1
Esempio n. 25
0
def info(input, to_json):
    """Return info about the mosaic."""
    with MosaicBackend(input) as mosaic:
        _info = {
            "Path": input,
            "Backend": mosaic._backend_name,
            "File Size": mosaic._file_byte_size,
            "Compressed": True if input.endswith(".gz") else False,
        }

        profile = {
            "MosaicJSON": mosaic.mosaic_def.mosaicjson,
            "Version": mosaic.mosaic_def.version,
            "Name": mosaic.mosaic_def.name,
            "Description": mosaic.mosaic_def.description,
            "Attribution": mosaic.mosaic_def.attribution,
        }

        geo = {
            "TileMatrixSet": "WebMercatorQuad",
            "BoundingBox": mosaic.mosaic_def.bounds,
            "Center": mosaic.mosaic_def.center,
            "Min Zoom": mosaic.mosaic_def.minzoom,
            "Max Zoom": mosaic.mosaic_def.maxzoom,
            "QuadKey Zoom": mosaic.mosaic_def.quadkey_zoom,
        }

        tiles = {}
        mosaic_tiles = mosaic.mosaic_def.tiles
        if mosaic_tiles:
            tiles["Nb Tiles"] = len(mosaic_tiles.keys())
            file_numb = [len(t) for t in mosaic_tiles.values()]
            tiles["Min Files"] = min(file_numb)
            tiles["Max Files"] = max(file_numb)
            tiles["Mean Files"] = round(sum(file_numb) / len(file_numb), 2)

    if to_json:
        output = _info.copy()
        output["Profile"] = profile
        output["GEO"] = geo
        output["TILES"] = tiles
        click.echo(json.dumps(output))
    else:
        sep = 25
        click.echo(
            f"""{click.style('File:', bold=True)} {_info['Path']}
{click.style('Backend:', bold=True)} {_info['Backend']}
{click.style('File Size:', bold=True)} {_info['File Size']}
{click.style('Compressed:', bold=True)} {_info['Compressed']}

{click.style('Profile', bold=True)}
    {click.style("MosaicJSON:", bold=True):<{sep}} {profile['MosaicJSON']}
    {click.style("Version:", bold=True):<{sep}} {profile['Version']}
    {click.style("Name:", bold=True):<{sep}} {profile['Name']}
    {click.style("Description:", bold=True):<{sep}} {profile['Description']}
    {click.style("Attribution:", bold=True):<{sep}} {profile['Attribution']}

{click.style('Geo', bold=True)}
    {click.style("TileMatrixSet:", bold=True):<{sep}} {geo['TileMatrixSet']}
    {click.style("BoundingBox:", bold=True):<{sep}} {geo['BoundingBox']}
    {click.style("Center:", bold=True):<{sep}} {geo['Center']}
    {click.style("Min Zoom:", bold=True):<{sep}} {geo['Min Zoom']}
    {click.style("Max Zoom:", bold=True):<{sep}} {geo['Max Zoom']}
    {click.style("QuadKey Zoom:", bold=True):<{sep}} {geo['QuadKey Zoom']}

{click.style('Tiles', bold=True)}
    {click.style("Nb Tiles:", bold=True):<{sep}} {tiles.get('Nb Tiles')}
    {click.style("Min Files:", bold=True):<{sep}} {tiles.get('Min Files')}
    {click.style("Max Files:", bold=True):<{sep}} {tiles.get('Max Files')}
    {click.style("Mean Files:", bold=True):<{sep}} {tiles.get('Mean Files')}"""
        )
Esempio n. 26
0
def create(
    url: str,
    bounds: str,
    min_cloud: float = 0,
    max_cloud: float = 100,
    min_date="2013-01-01",
    max_date=datetime.strftime(datetime.today(), "%Y-%m-%d"),
    period: str = None,
    period_qty: int = 1,
    seasons: str = None,
    minzoom: int = 7,
    maxzoom: int = 12,
    quadkey_zoom: int = 8,
    tile_format: str = "jpg",
    tile_scale: int = 1,
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /create requests.

    Args:
        - bounds: Comma-separated bounding box: "west, south, east, north"
        - min_cloud: Minimum cloud percentage
        - max_cloud: Maximum cloud percentage
        - min_date: Minimum date, inclusive
        - max_date: Maximum date, inclusive
        - period: Time period. If provided, overwrites `max-date` with the given period after `min-date`. Choice of 'day', 'week', 'month', 'year'
        - period_qty: Number of periods to apply after `min-date`. Only applies if `period` is provided
        - seasons, can provide multiple. Choice of 'spring', 'summer', 'autumn', 'winter'
    """
    period_choices = ["day", "week", "month", "year"]
    if period and period not in period_choices:
        return ("NOK", "text/plain", f"Period must be one of {period_choices}")

    min_cloud = float(min_cloud)
    max_cloud = float(max_cloud)
    minzoom = int(minzoom)
    maxzoom = int(maxzoom)
    bounds = tuple(map(float, bounds.split(",")))

    if seasons:
        seasons = seasons.split(",")
    else:
        seasons = None

    mosaicid = get_hash(
        bounds=bounds,
        min_cloud=min_cloud,
        max_cloud=max_cloud,
        min_date=min_date,
        max_date=max_date,
        period=period,
        period_qty=period_qty,
        minzoom=minzoom,
        maxzoom=maxzoom,
        seasons=seasons,
    )

    # Replace {mosaicid} template in url
    if "{mosaicid}" in url:
        url = url.replace("{mosaicid}", mosaicid)

    # Load mosaic if it already exists
    try:
        with MosaicBackend(url) as mosaic:
            mosaic_def = dict(mosaic.mosaic_def)

        return get_tilejson(
            mosaic_def,
            url,
            tile_scale,
            tile_format,
            host=app.host,
            path="/tiles",
            **kwargs,
        )

    except Exception:
        pass

    features = search(
        bounds=bounds,
        min_cloud=min_cloud,
        max_cloud=max_cloud,
        min_date=min_date,
        max_date=max_date,
        period=period,
        period_qty=period_qty,
    )

    if seasons:
        features = filter_season(features, seasons)

    if not features:
        return ("NOK", "text/plain", "No assets found for query")

    mosaic_def = features_to_mosaicJSON(features=features,
                                        quadkey_zoom=quadkey_zoom,
                                        minzoom=minzoom,
                                        maxzoom=maxzoom)

    with MosaicBackend(url, mosaic_def=mosaic_def) as mosaic:
        mosaic.write()

    return get_tilejson(mosaic_def,
                        url,
                        tile_scale,
                        tile_format,
                        host=app.host,
                        path="/tiles",
                        **kwargs)