Пример #1
0
def test_cog_translate_web_align():
    """
    Test Web-Optimized COG.

    - Test COG bounds (thus block) is aligned with Zoom levels

    """
    tms = morecantile.tms.get("WebMercatorQuad")

    runner = CliRunner()
    with runner.isolated_filesystem():

        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="256")

        with rasterio.open(raster_path_web) as src_dst:
            _, max_zoom = get_zooms(src_dst)

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
            aligned_levels=2,
        )
        with COGReader(raster_path_web) as src_dst:
            bounds = src_dst.bounds
            with COGReader("cogeo.tif") as cog:
                _, max_zoom = get_zooms(cog.dataset)
                ulTile = tms.xy_bounds(tms.tile(bounds[0], bounds[3], max_zoom - 2))
                assert round(cog.dataset.bounds[0], 5) == round(ulTile.left, 5)
                assert round(cog.dataset.bounds[3], 5) == round(ulTile.top, 5)

                lrTile = tms.xy_bounds(tms.tile(bounds[2], bounds[1], max_zoom - 2))
                assert round(cog.dataset.bounds[2], 5) == round(lrTile.right, 5)
                assert round(cog.dataset.bounds[1], 5) == round(lrTile.bottom, 5)

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
            aligned_levels=3,
        )
        with COGReader(raster_path_web) as src_dst:
            bounds = src_dst.bounds
            with COGReader("cogeo.tif") as cog_dst:
                _, max_zoom = get_zooms(cog_dst.dataset)
                ulTile = tms.xy_bounds(tms.tile(bounds[0], bounds[3], max_zoom - 3))
                assert round(cog_dst.dataset.bounds[0], 5) == round(ulTile.left, 5)
                assert round(cog_dst.dataset.bounds[3], 5) == round(ulTile.top, 5)

                lrTile = tms.xy_bounds(tms.tile(bounds[2], bounds[1], max_zoom - 3))
                assert round(cog_dst.dataset.bounds[2], 5) == round(lrTile.right, 5)
                assert round(cog_dst.dataset.bounds[1], 5) == round(lrTile.bottom, 5)
Пример #2
0
def test_cog_translate_webZooms():
    """
    Test Web-Optimized COG.

    - Test COG size is a multiple of 256 (mercator tile size)
    - Test COG bounds are aligned with mercator grid at max zoom
    - Test high resolution internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    - Test overview internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    """

    runner = CliRunner()
    with runner.isolated_filesystem():
        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="128")

        cog_translate(
            raster_path_north,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
        )
        with rasterio.open("cogeo.tif") as out_dst:
            _, maxzoom = get_zooms(out_dst)
            assert maxzoom == 8

        cog_translate(
            raster_path_north,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            latitude_adjustment=False,
            config=config,
        )
        with rasterio.open("cogeo.tif") as out_dst:
            _, maxzoom = get_zooms(out_dst)
            assert maxzoom == 10
Пример #3
0
def test_cog_translate_web():
    """
    Test Web-Optimized COG.

    - Test COG size is a multiple of 256 (mercator tile size)
    - Test COG bounds are aligned with mercator grid at max zoom
    """
    tms = morecantile.tms.get("WebMercatorQuad")

    runner = CliRunner()
    with runner.isolated_filesystem():

        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="256")

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
            aligned_levels=0,
        )
        with rasterio.open(raster_path_web) as src_dst:
            with rasterio.open("cogeo.tif") as out_dst:
                blocks = list(set(out_dst.block_shapes))
                assert len(blocks) == 1
                ts = blocks[0][0]
                assert not out_dst.width % ts
                assert not out_dst.height % ts
                _, max_zoom = get_zooms(out_dst)

                bounds = list(
                    transform_bounds(src_dst.crs,
                                     "epsg:4326",
                                     *src_dst.bounds,
                                     densify_pts=21))
                ulTile = tms.xy_bounds(tms.tile(bounds[0], bounds[3],
                                                max_zoom))
                assert out_dst.bounds.left == ulTile.left
                assert out_dst.bounds.top == ulTile.top

                lrTile = tms.xy_bounds(tms.tile(bounds[2], bounds[1],
                                                max_zoom))
                assert out_dst.bounds.right == lrTile.right
                assert round(out_dst.bounds.bottom,
                             6) == round(lrTile.bottom, 6)
Пример #4
0
def test_cogeo_zoom_level(runner):
    """Should work as expected."""
    with runner.isolated_filesystem():
        result = runner.invoke(
            cogeo,
            [
                "create",
                raster_path_rgb,
                "output.tif",
                "--web-optimized",
                "--zoom-level",
                "18",
            ],
        )
        assert not result.exception
        assert result.exit_code == 0
        with rasterio.open("output.tif") as src:
            _, max_zoom = get_zooms(src)
            assert max_zoom == 18

        result = runner.invoke(
            cogeo,
            [
                "create",
                raster_path_rgb,
                "output.tif",
                "--web-optimized",
                "--zoom-level",
                "19",
            ],
        )
        assert not result.exception
        assert result.exit_code == 0
        with rasterio.open("output.tif") as src:
            _, max_zoom = get_zooms(src)
            assert max_zoom == 19
Пример #5
0
def cog_info(src_path: Union[str, pathlib.PurePath], **kwargs: Any) -> Dict:
    """Get general info and validate Cloud Optimized Geotiff."""
    is_valid, validation_errors, validation_warnings = cog_validate(
        src_path,
        quiet=True,
        **kwargs,
    )

    with rasterio.open(src_path) as src_dst:
        _info = {
            "Path":
            str(src_path),
            "Driver":
            src_dst.driver,
            "COG":
            is_valid,
            "Compression":
            src_dst.compression.value if src_dst.compression else None,
            "ColorSpace":
            src_dst.photometric.value if src_dst.photometric else None,
        }
        if validation_errors:
            _info["COG_errors"] = validation_errors

        if validation_warnings:
            _info["COG_warnings"] = validation_warnings

        try:
            colormap = src_dst.colormap(1)
        except ValueError:
            colormap = None

        profile = {
            "Bands": src_dst.count,
            "Width": src_dst.width,
            "Height": src_dst.height,
            "Tiled": src_dst.is_tiled,
            "Dtype": src_dst.dtypes[0],
            "Interleave": src_dst.interleaving.value,
            "Alpha Band": utils.has_alpha_band(src_dst),
            "Internal Mask": utils.has_mask_band(src_dst),
            "Nodata": src_dst.nodata,
            "ColorInterp":
            tuple([color.name for color in src_dst.colorinterp]),
            "ColorMap": colormap is not None,
            "Scales": src_dst.scales,
            "Offsets": src_dst.offsets,
        }
        try:
            crs = (f"EPSG:{src_dst.crs.to_epsg()}"
                   if src_dst.crs.to_epsg() else src_dst.crs.to_wkt())
        except AttributeError:
            crs = None

        minzoom: Optional[int] = None
        maxzoom: Optional[int] = None
        try:
            minzoom, maxzoom = utils.get_zooms(src_dst)
        except Exception:
            pass

        geo = {
            "CRS": crs,
            "BoundingBox": tuple(src_dst.bounds),
            "Origin": (src_dst.transform.c, src_dst.transform.f),
            "Resolution": (src_dst.transform.a, src_dst.transform.e),
            "MinZoom": minzoom,
            "MaxZoom": maxzoom,
        }

        ifd_raw = [{
            "Level": 0,
            "Width": src_dst.width,
            "Height": src_dst.height,
            "Blocksize": src_dst.block_shapes[0],
            "Decimation": 0,
        }]
        overviews = src_dst.overviews(1)
        tags = src_dst.tags()

    ifd_ovr = []
    for ix, decim in enumerate(overviews):
        with rasterio.open(src_path, OVERVIEW_LEVEL=ix) as ovr_dst:
            ifd_ovr.append({
                "Level": ix + 1,
                "Width": ovr_dst.width,
                "Height": ovr_dst.height,
                "Blocksize": ovr_dst.block_shapes[0],
                "Decimation": decim,
            })

    ifds = ifd_raw + ifd_ovr
    output = _info.copy()
    output["Profile"] = profile
    output["GEO"] = geo
    output["Tags"] = tags
    output["IFD"] = ifds

    return output
Пример #6
0
def test_cog_translate_Internal():
    """
    Test Web-Optimized COG.

    - Test COG size is a multiple of 256 (mercator tile size)
    - Test COG bounds are aligned with mercator grid at max zoom
    - Test high resolution internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    - Test overview internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    """
    tms = morecantile.tms.get("WebMercatorQuad")

    runner = CliRunner()
    with runner.isolated_filesystem():

        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="256")

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
            aligned_levels=0,
        )
        with rasterio.open(raster_path_web) as src_dst:
            with rasterio.open("cogeo.tif") as out_dst:
                blocks = list(set(out_dst.block_shapes))
                assert len(blocks) == 1
                ts = blocks[0][0]
                assert not out_dst.width % ts
                assert not out_dst.height % ts
                _, max_zoom = get_zooms(out_dst)

                bounds = list(
                    transform_bounds(src_dst.crs,
                                     "epsg:4326",
                                     *src_dst.bounds,
                                     densify_pts=21))

                minimumTile = tms.tile(bounds[0], bounds[3], max_zoom)
                maximumTile = tms.tile(bounds[2], bounds[1], max_zoom)

                with open("cogeo.tif", "rb") as out_body:
                    reader = FileReader(out_body)
                    cog = COGTiff(reader.read)

                    # High resolution
                    # Top Left tile
                    _, tile = cog.get_tile(0, 0, 0)
                    tile_length = 256 * 256 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr = numpy.array(t).reshape(256, 256,
                                                 3).astype(numpy.uint8)
                    arr = numpy.transpose(arr, [2, 0, 1])

                    with rasterio.open("cogeo.tif") as src_dst:
                        w = from_bounds(*tms.xy_bounds(minimumTile),
                                        src_dst.transform)
                        data = src_dst.read(window=w,
                                            out_shape=(src_dst.count, 256,
                                                       256))

                    numpy.testing.assert_array_equal(data, arr)

                    # Bottom right tile
                    _, tile = cog.get_tile(4, 3, 0)
                    tile_length = 256 * 256 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr = numpy.array(t).reshape(256, 256,
                                                 3).astype(numpy.uint8)
                    arr = numpy.transpose(arr, [2, 0, 1])

                    with rasterio.open("cogeo.tif") as src_dst:
                        w = from_bounds(*tms.xy_bounds(maximumTile),
                                        src_dst.transform)
                        data = src_dst.read(window=w,
                                            out_shape=(src_dst.count, 256,
                                                       256))
                        numpy.testing.assert_array_equal(data, arr)
Пример #7
0
def test_cog_translate_Internal():
    """
    Test Web-Optimized COG.

    - Test COG size is a multiple of 256 (mercator tile size)
    - Test COG bounds are aligned with mercator grid at max zoom
    - Test high resolution internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    - Test overview internal tiles are equal to mercator tile using
      cogdumper and rio-tiler
    """
    from cogdumper.cog_tiles import COGTiff
    from cogdumper.filedumper import Reader as FileReader

    runner = CliRunner()
    with runner.isolated_filesystem():

        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="128")

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
        )
        with rasterio.open(raster_path_web) as src_dst:
            with rasterio.open("cogeo.tif") as out_dst:
                blocks = list(set(out_dst.block_shapes))
                assert len(blocks) == 1
                ts = blocks[0][0]
                assert not out_dst.width % ts
                assert not out_dst.height % ts
                _, max_zoom = get_zooms(out_dst)

                bounds = list(
                    transform_bounds(src_dst.crs,
                                     "epsg:4326",
                                     *src_dst.bounds,
                                     densify_pts=21))

                minimumTile = mercantile.tile(bounds[0], bounds[3], max_zoom)
                maximumTile = mercantile.tile(bounds[2], bounds[1], max_zoom)

                with open("cogeo.tif", "rb") as out_body:
                    reader = FileReader(out_body)
                    cog = COGTiff(reader.read)

                    # High resolution
                    # Top Left tile
                    _, tile = cog.get_tile(0, 0, 0)
                    tile_length = 256 * 256 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr = numpy.array(t).reshape(256, 256,
                                                 3).astype(numpy.uint8)
                    arr = numpy.transpose(arr, [2, 0, 1])

                    with rasterio.open("cogeo.tif") as src_dst:
                        data, _ = COGreader.tile(src_dst,
                                                 *minimumTile,
                                                 resampling_method="nearest")
                    numpy.testing.assert_array_equal(data, arr)

                    # Bottom right tile
                    _, tile = cog.get_tile(4, 3, 0)
                    tile_length = 256 * 256 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr = numpy.array(t).reshape(256, 256,
                                                 3).astype(numpy.uint8)
                    arr = numpy.transpose(arr, [2, 0, 1])

                    with rasterio.open("cogeo.tif") as src_dst:
                        data, _ = COGreader.tile(src_dst,
                                                 *maximumTile,
                                                 resampling_method="nearest")
                    numpy.testing.assert_array_equal(data, arr)

                    # Low resolution (overview 1)
                    # Top Left tile
                    # NOTE: overview internal tile size is 128px
                    # We need to stack two internal tiles to compare with
                    # the 256px mercator tile fetched by rio-tiler
                    # ref: https://github.com/cogeotiff/rio-cogeo/issues/60
                    _, tile = cog.get_tile(1, 0, 1)
                    tile_length = 128 * 128 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr1 = numpy.array(t).reshape(128, 128,
                                                  3).astype(numpy.uint8)
                    arr1 = numpy.transpose(arr1, [2, 0, 1])

                    _, tile = cog.get_tile(2, 0, 1)
                    tile_length = 128 * 128 * 3
                    t = struct.unpack_from("{}b".format(tile_length), tile)
                    arr2 = numpy.array(t).reshape(128, 128,
                                                  3).astype(numpy.uint8)
                    arr2 = numpy.transpose(arr2, [2, 0, 1])
                    arr = numpy.dstack((arr1, arr2))

                    with rasterio.open("cogeo.tif") as src_dst:
                        data, _ = COGreader.tile(src_dst,
                                                 118594,
                                                 60034,
                                                 17,
                                                 resampling_method="nearest")

                    data = data[:, 128:, :]
                    numpy.testing.assert_array_equal(data, arr)
Пример #8
0
def cog_info(src_path: Union[str, pathlib.PurePath],
             **kwargs: Any) -> models.Info:
    """Get general info and validate Cloud Optimized Geotiff."""
    is_valid, validation_errors, validation_warnings = cog_validate(
        src_path,
        quiet=True,
        **kwargs,
    )

    with rasterio.open(src_path) as src_dst:
        driver = src_dst.driver
        compression = src_dst.compression.value if src_dst.compression else None
        colorspace = src_dst.photometric.value if src_dst.photometric else None
        overviews = src_dst.overviews(1)

        tags = {"Image Metadata": src_dst.tags()}
        namespaces = src_dst.tag_namespaces()
        for ns in namespaces:
            if ns in ["DERIVED_SUBDATASETS"]:
                continue
            tags.update({str.title(ns).replace("_", " "): src_dst.tags(ns=ns)})

        band_metadata = {
            f"Band {ix}": models.BandMetadata(
                **{
                    "Description": src_dst.descriptions[ix - 1],
                    "ColorInterp": src_dst.colorinterp[ix - 1].name,
                    "Offset": src_dst.offsets[ix - 1],
                    "Scale": src_dst.scales[ix - 1],
                    "Metadata": src_dst.tags(ix),
                })
            for ix in src_dst.indexes
        }

        try:
            colormap = src_dst.colormap(1)
        except ValueError:
            colormap = None

        profile = models.Profile(
            Bands=src_dst.count,
            Width=src_dst.width,
            Height=src_dst.height,
            Tiled=src_dst.is_tiled,
            Dtype=src_dst.dtypes[0],
            Interleave=src_dst.interleaving.value,
            AlphaBand=utils.has_alpha_band(src_dst),
            InternalMask=utils.has_mask_band(src_dst),
            Nodata=src_dst.nodata,
            ColorInterp=tuple([color.name for color in src_dst.colorinterp]),
            ColorMap=colormap is not None,
            Scales=src_dst.scales,
            Offsets=src_dst.offsets,
        )

        try:
            crs = (f"EPSG:{src_dst.crs.to_epsg()}"
                   if src_dst.crs.to_epsg() else src_dst.crs.to_wkt())
        except AttributeError:
            crs = None

        minzoom: Optional[int] = None
        maxzoom: Optional[int] = None
        try:
            minzoom, maxzoom = utils.get_zooms(src_dst)
        except Exception:
            pass

        geo = models.Geo(
            CRS=crs,
            BoundingBox=tuple(src_dst.bounds),
            Origin=(src_dst.transform.c, src_dst.transform.f),
            Resolution=(src_dst.transform.a, src_dst.transform.e),
            MinZoom=minzoom,
            MaxZoom=maxzoom,
        )

        ifds = [
            models.IFD(
                Level=0,
                Width=src_dst.width,
                Height=src_dst.height,
                Blocksize=src_dst.block_shapes[0],
                Decimation=0,
            )
        ]

    for ix, decim in enumerate(overviews):
        with rasterio.open(src_path, OVERVIEW_LEVEL=ix) as ovr_dst:
            ifds.append(
                models.IFD(
                    Level=ix + 1,
                    Width=ovr_dst.width,
                    Height=ovr_dst.height,
                    Blocksize=ovr_dst.block_shapes[0],
                    Decimation=decim,
                ))

    return models.Info(
        Path=str(src_path),
        Driver=driver,
        COG=is_valid,
        Compression=compression,
        ColorSpace=colorspace,
        COG_errors=validation_errors or None,
        COG_warnings=validation_warnings or None,
        Profile=profile,
        GEO=geo,
        Tags=tags,
        Band_Metadata=band_metadata,
        IFD=ifds,
    )