Exemplo n.º 1
0
def test_overviewlevel_valid():
    """Should work as expected (return overview level)."""
    assert get_maximum_overview_level(raster_path_rgb, 128) == 2
Exemplo n.º 2
0
def test_overviewlevel_valid():
    """Should work as expected (return overview level)."""
    with rasterio.open(raster_path_rgb) as src_dst:
        assert get_maximum_overview_level(src_dst, 128) == 2
        assert get_maximum_overview_level(src_dst) == 0
Exemplo n.º 3
0
def cog_translate(
    src_path,
    dst_path,
    dst_kwargs,
    indexes=None,
    nodata=None,
    add_mask=None,
    overview_level=None,
    overview_resampling="nearest",
    config=None,
    quiet=False,
):
    """
    Create Cloud Optimized Geotiff.

    Parameters
    ----------
    src_path : str or PathLike object
        A dataset path or URL. Will be opened in "r" mode.
    dst_path : str or Path-like object
        An output dataset path or or PathLike object.
        Will be opened in "w" mode.
    dst_kwargs: dict
        output dataset creation options.
    indexes : tuple, int, optional
        Raster band indexes to copy.
    nodata, int, optional
        Overwrite nodata masking values for input dataset.
    add_mask, bool, optional
        Force output dataset creation with a mask.
    overview_level : int, optional (default: 6)
        COGEO overview (decimation) level
    overview_resampling : str, optional (default: "nearest")
        Resampling algorithm for overviews
    config : dict
        Rasterio Env options.
    quiet: bool, optional (default: False)
        Mask processing steps.

    """
    config = config or {}

    if overview_level is None:
        overview_level = get_maximum_overview_level(
            src_path,
            min(int(dst_kwargs["blockxsize"]), int(dst_kwargs["blockysize"])))

    with rasterio.Env(**config):
        with rasterio.open(src_path) as src_dst:
            meta = src_dst.meta
            indexes = indexes if indexes else src_dst.indexes
            nodata = nodata if nodata is not None else src_dst.nodata
            alpha = has_alpha_band(src_dst)
            mask = has_mask_band(src_dst)

            if not add_mask and (
                (nodata is not None or alpha)
                    and dst_kwargs.get("compress") in ["JPEG", "jpeg"]):
                warnings.warn(
                    "Using lossy compression with Nodata or Alpha band "
                    "can results in unwanted artefacts.",
                    LossyCompression,
                )

            vrt_params = dict(add_alpha=True)

            if nodata is not None:
                vrt_params.update(
                    dict(nodata=nodata, add_alpha=False, src_nodata=nodata))

            if alpha:
                vrt_params.update(dict(add_alpha=False))

            with WarpedVRT(src_dst, **vrt_params) as vrt_dst:
                meta = vrt_dst.meta
                meta["count"] = len(indexes)

                if add_mask:
                    meta.pop("nodata", None)
                    meta.pop("alpha", None)

                meta.update(**dst_kwargs)
                meta.pop("compress", None)
                meta.pop("photometric", None)

                with MemoryFile() as memfile:
                    with memfile.open(**meta) as mem:
                        wind = list(mem.block_windows(1))

                        if not quiet:
                            click.echo("Reading input: {}".format(src_path),
                                       err=True)
                        fout = os.devnull if quiet else sys.stderr
                        with click.progressbar(wind,
                                               length=len(wind),
                                               file=fout,
                                               show_percent=True) as windows:
                            for ij, w in windows:
                                matrix = vrt_dst.read(window=w,
                                                      indexes=indexes)
                                mem.write(matrix, window=w)

                                if add_mask or mask:
                                    mask_value = vrt_dst.dataset_mask(window=w)
                                    mem.write_mask(mask_value, window=w)

                        if not quiet:
                            click.echo("Adding overviews...", err=True)
                        overviews = [
                            2**j for j in range(1, overview_level + 1)
                        ]
                        mem.build_overviews(overviews,
                                            Resampling[overview_resampling])

                        if not quiet:
                            click.echo("Updating dataset tags...", err=True)

                        for i, b in enumerate(indexes):
                            mem.set_band_description(
                                i + 1, src_dst.descriptions[b - 1])

                        tags = src_dst.tags()
                        tags.update(
                            dict(OVR_RESAMPLING_ALG=Resampling[
                                overview_resampling].name.upper()))
                        mem.update_tags(**tags)

                        if not quiet:
                            click.echo(
                                "Writing output to: {}".format(dst_path),
                                err=True)
                        copy(mem,
                             dst_path,
                             copy_src_overviews=True,
                             **dst_kwargs)
Exemplo n.º 4
0
def cog_translate(
    src_path,
    dst_path,
    dst_kwargs,
    indexes=None,
    nodata=None,
    alpha=None,
    overview_level=None,
    overview_resampling="nearest",
    config=None,
):
    """
    Create Cloud Optimized Geotiff.

    Parameters
    ----------
    src_path : str or PathLike object
        A dataset path or URL. Will be opened in "r" mode.
    dst_path : str or Path-like object
        An output dataset path or or PathLike object.
        Will be opened in "w" mode.
    dst_kwargs: dict
        output dataset creation options.
    indexes : tuple, int, optional
        Raster band indexes to copy.
    nodata, int, optional
        nodata value for mask creation.
    alpha, int, optional
        alpha band index for mask creation.
    overview_level : int, optional (default: 6)
        COGEO overview (decimation) level
    overview_resampling : str, optional (default: "nearest")
         Resampling algorithm for overviews
    config : dict
        Rasterio Env options.

    """
    config = config or {}

    if overview_level is None:
        overview_level = get_maximum_overview_level(
            src_path,
            min(int(dst_kwargs["blockxsize"]), int(dst_kwargs["blockysize"])))

    with rasterio.Env(**config):
        with rasterio.open(src_path) as src:

            indexes = indexes if indexes else src.indexes
            meta = src.meta
            meta["count"] = len(indexes)
            meta.pop("nodata", None)
            meta.pop("alpha", None)

            meta.update(**dst_kwargs)
            meta.pop("compress", None)
            meta.pop("photometric", None)

            with MemoryFile() as memfile:
                with memfile.open(**meta) as mem:
                    wind = list(mem.block_windows(1))
                    with click.progressbar(wind,
                                           length=len(wind),
                                           file=sys.stderr,
                                           show_percent=True) as windows:
                        for ij, w in windows:
                            matrix = src.read(window=w, indexes=indexes)
                            mem.write(matrix, window=w)

                            if nodata is not None and not numpy.isfinite(
                                    nodata):
                                mask_value = (
                                    numpy.all(numpy.isfinite(matrix),
                                              axis=0).astype(numpy.uint8) *
                                    255)
                            elif nodata is not None:
                                mask_value = (
                                    numpy.all(matrix != nodata, axis=0).astype(
                                        numpy.uint8) * 255)
                            elif alpha is not None:
                                mask_value = src.read(alpha, window=w)
                            else:
                                mask_value = src.dataset_mask(window=w)
                            mem.write_mask(mask_value, window=w)

                    overviews = [2**j for j in range(1, overview_level + 1)]

                    mem.build_overviews(overviews,
                                        Resampling[overview_resampling])
                    mem.update_tags(
                        OVR_RESAMPLING_ALG=Resampling[overview_resampling].
                        name.upper())

                    copy(mem, dst_path, copy_src_overviews=True, **dst_kwargs)
Exemplo n.º 5
0
def cog_translate(
    source,
    dst_path,
    dst_kwargs,
    indexes=None,
    nodata=None,
    dtype=None,
    add_mask=None,
    overview_level=None,
    overview_resampling="nearest",
    web_optimized=False,
    latitude_adjustment=True,
    resampling="nearest",
    in_memory=None,
    config=None,
    allow_intermediate_compression=False,
    forward_band_tags=False,
    quiet=False,
):
    """
    Create Cloud Optimized Geotiff.

    Parameters
    ----------
    source : str, PathLike object or rasterio.io.DatasetReader
        A dataset path, URL or rasterio.io.DatasetReader object.
        Will be opened in "r" mode.
    dst_path : str or Path-like object
        An output dataset path or or PathLike object.
        Will be opened in "w" mode.
    dst_kwargs: dict
        Output dataset creation options.
    indexes : tuple or int, optional
        Raster band indexes to copy.
    nodata, int, optional
        Overwrite nodata masking values for input dataset.
    dtype: str, optional
        Overwrite output data type. Default will be the input data type.
    add_mask, bool, optional
        Force output dataset creation with a mask.
    overview_level : int, optional (default: 6)
        COGEO overview (decimation) level
    overview_resampling : str, optional (default: "nearest")
        Resampling algorithm for overviews
    web_optimized: bool, option (default: False)
        Create web-optimized cogeo.
    latitude_adjustment: bool, option (default: True)
        Use mercator meters for zoom calculation or ensure max zoom equality.
    resampling : str, optional (default: "nearest")
        Resampling algorithm.
    in_memory: bool, optional
        Force processing raster in memory (default: process in memory if small)
    config : dict
        Rasterio Env options.
    allow_intermediate_compression: bool, optional (default: False)
        Allow intermediate file compression to reduce memory/disk footprint.
        Note: This could reduce the speed of the process.
        Ref: https://github.com/cogeotiff/rio-cogeo/issues/103
    forward_band_tags:  bool, optional
        Forward band tags to output bands.
        Ref: https://github.com/cogeotiff/rio-cogeo/issues/19
    quiet: bool, optional (default: False)
        Mask processing steps.

    """
    if isinstance(indexes, int):
        indexes = (indexes, )

    config = config or {}
    with rasterio.Env(**config):
        with ExitStack() as ctx:
            if isinstance(source, (DatasetReader, DatasetWriter, WarpedVRT)):
                src_dst = source
            else:
                src_dst = ctx.enter_context(rasterio.open(source))

            meta = src_dst.meta
            indexes = indexes if indexes else src_dst.indexes
            nodata = nodata if nodata is not None else src_dst.nodata
            dtype = dtype if dtype else src_dst.dtypes[0]
            alpha = has_alpha_band(src_dst)
            mask = has_mask_band(src_dst)

            if not add_mask and (
                (nodata is not None or alpha)
                    and dst_kwargs.get("compress") in ["JPEG", "jpeg"]):
                warnings.warn(
                    "Using lossy compression with Nodata or Alpha band "
                    "can results in unwanted artefacts.",
                    LossyCompression,
                )

            tilesize = min(int(dst_kwargs["blockxsize"]),
                           int(dst_kwargs["blockysize"]))

            if src_dst.width < tilesize or src_dst.height < tilesize:
                tilesize = 2**int(
                    math.log(min(src_dst.width, src_dst.height), 2))
                if tilesize < 64:
                    warnings.warn(
                        "Raster has dimension < 64px. Output COG cannot be tiled"
                        " and overviews cannot be added.",
                        IncompatibleBlockRasterSize,
                    )
                    dst_kwargs.pop("blockxsize", None)
                    dst_kwargs.pop("blockysize", None)
                    dst_kwargs.pop("tiled")
                    overview_level = 0

                else:
                    warnings.warn(
                        "Block Size are bigger than raster sizes. "
                        "Setting blocksize to {}".format(tilesize),
                        IncompatibleBlockRasterSize,
                    )
                    dst_kwargs["blockxsize"] = tilesize
                    dst_kwargs["blockysize"] = tilesize

            vrt_params = dict(add_alpha=True, dtype=dtype)

            if nodata is not None:
                vrt_params.update(
                    dict(nodata=nodata, add_alpha=False, src_nodata=nodata))

            if alpha:
                vrt_params.update(dict(add_alpha=False))

            if web_optimized:
                bounds = list(
                    transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                     list(src_dst.bounds),
                                     densify_pts=21))
                center = [(bounds[0] + bounds[2]) / 2,
                          (bounds[1] + bounds[3]) / 2]

                lat = 0 if latitude_adjustment else center[1]
                max_zoom = get_max_zoom(src_dst, lat=lat, tilesize=tilesize)

                extrema = tile_extrema(bounds, max_zoom)
                w, n = mercantile.xy(*mercantile.ul(
                    extrema["x"]["min"], extrema["y"]["min"], max_zoom))
                vrt_res = _meters_per_pixel(max_zoom, 0, tilesize=tilesize)
                vrt_transform = Affine(vrt_res, 0, w, 0, -vrt_res, n)

                vrt_width = (extrema["x"]["max"] -
                             extrema["x"]["min"]) * tilesize
                vrt_height = (extrema["y"]["max"] -
                              extrema["y"]["min"]) * tilesize

                vrt_params.update(
                    dict(
                        crs="epsg:3857",
                        transform=vrt_transform,
                        width=vrt_width,
                        height=vrt_height,
                        resampling=ResamplingEnums[resampling],
                    ))

            with WarpedVRT(src_dst, **vrt_params) as vrt_dst:
                meta = vrt_dst.meta
                meta["count"] = len(indexes)

                if add_mask:
                    meta.pop("nodata", None)
                    meta.pop("alpha", None)

                if (dst_kwargs.get("photometric", "").upper() == "YCBCR"
                        and meta["count"] == 1):
                    warnings.warn(
                        "PHOTOMETRIC=YCBCR not supported on a 1-band raster"
                        " and has been set to 'MINISBLACK'")
                    dst_kwargs["photometric"] = "MINISBLACK"

                meta.update(**dst_kwargs)
                if not allow_intermediate_compression:
                    meta.pop("compress", None)
                    meta.pop("photometric", None)

                if in_memory is None:
                    in_memory = vrt_dst.width * vrt_dst.height < IN_MEMORY_THRESHOLD

                if in_memory:
                    tmpfile = ctx.enter_context(MemoryFile())
                    tmp_dst = ctx.enter_context(tmpfile.open(**meta))
                else:
                    tmpfile = ctx.enter_context(TemporaryRasterFile(dst_path))
                    tmp_dst = ctx.enter_context(
                        rasterio.open(tmpfile.name, "w", **meta))

                # Transfer color interpolation
                if len(indexes) == 1 and (vrt_dst.colorinterp[indexes[0] - 1]
                                          is not ColorInterp.palette):
                    tmp_dst.colorinterp = [ColorInterp.gray]
                else:
                    tmp_dst.colorinterp = [
                        vrt_dst.colorinterp[b - 1] for b in indexes
                    ]

                if tmp_dst.colorinterp[0] is ColorInterp.palette:
                    try:
                        tmp_dst.write_colormap(1, vrt_dst.colormap(1))
                    except ValueError:
                        warnings.warn(
                            "Dataset has `Palette` color interpretation"
                            " but is missing colormap information")

                wind = list(tmp_dst.block_windows(1))

                if not quiet:
                    click.echo("Reading input: {}".format(source), err=True)
                fout = os.devnull if quiet else sys.stderr
                with click.progressbar(wind,
                                       length=len(wind),
                                       file=fout,
                                       show_percent=True) as windows:
                    for ij, w in windows:
                        matrix = vrt_dst.read(window=w, indexes=indexes)
                        tmp_dst.write(matrix, window=w)

                        if add_mask or mask:
                            # Cast mask to uint8 to fix rasterio 1.1.2 error (ref #115)
                            mask_value = vrt_dst.dataset_mask(
                                window=w).astype("uint8")
                            tmp_dst.write_mask(mask_value, window=w)

                if overview_level is None:
                    overview_level = get_maximum_overview_level(
                        vrt_dst, tilesize)

                if not quiet and overview_level:
                    click.echo("Adding overviews...", err=True)

                overviews = [2**j for j in range(1, overview_level + 1)]
                tmp_dst.build_overviews(overviews,
                                        ResamplingEnums[overview_resampling])

                if not quiet:
                    click.echo("Updating dataset tags...", err=True)

                for i, b in enumerate(indexes):
                    tmp_dst.set_band_description(i + 1,
                                                 src_dst.descriptions[b - 1])
                    if forward_band_tags:
                        tmp_dst.update_tags(i + 1, **src_dst.tags(b))

                tags = src_dst.tags()
                tags.update(
                    dict(
                        OVR_RESAMPLING_ALG=ResamplingEnums[overview_resampling]
                        .name.upper()))
                tmp_dst.update_tags(**tags)
                tmp_dst._set_all_scales(
                    [vrt_dst.scales[b - 1] for b in indexes])
                tmp_dst._set_all_offsets(
                    [vrt_dst.offsets[b - 1] for b in indexes])

                if not quiet:
                    click.echo("Writing output to: {}".format(dst_path),
                               err=True)
                copy(tmp_dst, dst_path, copy_src_overviews=True, **dst_kwargs)
Exemplo n.º 6
0
def cog_translate(
    src_path,
    dst_path,
    dst_kwargs,
    indexes=None,
    nodata=None,
    add_mask=None,
    overview_level=None,
    overview_resampling="nearest",
    web_optimized=False,
    latitude_adjustment=True,
    resampling="nearest",
    in_memory=None,
    config=None,
    quiet=False,
):
    """
    Create Cloud Optimized Geotiff.

    Parameters
    ----------
    src_path : str or PathLike object
        A dataset path or URL. Will be opened in "r" mode.
    dst_path : str or Path-like object
        An output dataset path or or PathLike object.
        Will be opened in "w" mode.
    dst_kwargs: dict
        Output dataset creation options.
    indexes : tuple, int, optional
        Raster band indexes to copy.
    nodata, int, optional
        Overwrite nodata masking values for input dataset.
    add_mask, bool, optional
        Force output dataset creation with a mask.
    overview_level : int, optional (default: 6)
        COGEO overview (decimation) level
    overview_resampling : str, optional (default: "nearest")
        Resampling algorithm for overviews
    web_optimized: bool, option (default: False)
        Create web-optimized cogeo.
    latitude_adjustment: bool, option (default: True)
        Use mercator meters for zoom calculation or ensure max zoom equality.
    resampling : str, optional (default: "nearest")
        Resampling algorithm.
    in_memory: bool, optional
        Force processing raster in memory (default: process in memory if small)
    config : dict
        Rasterio Env options.
    quiet: bool, optional (default: False)
        Mask processing steps.

    """
    config = config or {}

    with rasterio.Env(**config):
        with rasterio.open(src_path) as src_dst:
            meta = src_dst.meta
            indexes = indexes if indexes else src_dst.indexes
            nodata = nodata if nodata is not None else src_dst.nodata
            alpha = has_alpha_band(src_dst)
            mask = has_mask_band(src_dst)

            if not add_mask and (
                (nodata is not None or alpha)
                    and dst_kwargs.get("compress") in ["JPEG", "jpeg"]):
                warnings.warn(
                    "Using lossy compression with Nodata or Alpha band "
                    "can results in unwanted artefacts.",
                    LossyCompression,
                )

            tilesize = min(int(dst_kwargs["blockxsize"]),
                           int(dst_kwargs["blockysize"]))

            if src_dst.width < tilesize or src_dst.height < tilesize:
                tilesize = 2**int(
                    math.log(min(src_dst.width, src_dst.height), 2))
                if tilesize < 64:
                    warnings.warn(
                        "Raster has dimension < 64px. Output COG cannot be tiled"
                        " and overviews cannot be added.",
                        IncompatibleBlockRasterSize,
                    )
                    dst_kwargs.pop("blockxsize", None)
                    dst_kwargs.pop("blockysize", None)
                    dst_kwargs.pop("tiled")
                    overview_level = 0

                else:
                    warnings.warn(
                        "Block Size are bigger than raster sizes. "
                        "Setting blocksize to {}".format(tilesize),
                        IncompatibleBlockRasterSize,
                    )
                    dst_kwargs["blockxsize"] = tilesize
                    dst_kwargs["blockysize"] = tilesize

            vrt_params = dict(add_alpha=True)

            if nodata is not None:
                vrt_params.update(
                    dict(nodata=nodata, add_alpha=False, src_nodata=nodata))

            if alpha:
                vrt_params.update(dict(add_alpha=False))

            if web_optimized:
                bounds = list(
                    transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                     list(src_dst.bounds),
                                     densify_pts=21))
                center = [(bounds[0] + bounds[2]) / 2,
                          (bounds[1] + bounds[3]) / 2]

                lat = 0 if latitude_adjustment else center[1]
                max_zoom = get_max_zoom(src_dst, lat=lat, tilesize=tilesize)

                extrema = tile_extrema(bounds, max_zoom)
                w, n = mercantile.xy(*mercantile.ul(
                    extrema["x"]["min"], extrema["y"]["min"], max_zoom))
                vrt_res = _meters_per_pixel(max_zoom, 0, tilesize=tilesize)
                vrt_transform = Affine(vrt_res, 0, w, 0, -vrt_res, n)

                vrt_width = (extrema["x"]["max"] -
                             extrema["x"]["min"]) * tilesize
                vrt_height = (extrema["y"]["max"] -
                              extrema["y"]["min"]) * tilesize

                vrt_params.update(
                    dict(
                        crs="epsg:3857",
                        transform=vrt_transform,
                        width=vrt_width,
                        height=vrt_height,
                        resampling=ResamplingEnums[resampling],
                    ))

            with WarpedVRT(src_dst, **vrt_params) as vrt_dst:
                meta = vrt_dst.meta
                meta["count"] = len(indexes)

                if add_mask:
                    meta.pop("nodata", None)
                    meta.pop("alpha", None)

                meta.update(**dst_kwargs)
                meta.pop("compress", None)
                meta.pop("photometric", None)

                if in_memory is None:
                    in_memory = vrt_dst.width * vrt_dst.height < IN_MEMORY_THRESHOLD

                with ExitStack() as ctx:
                    if in_memory:
                        tmpfile = ctx.enter_context(MemoryFile())
                        tmp_dst = ctx.enter_context(tmpfile.open(**meta))
                    else:
                        tmpfile = ctx.enter_context(
                            TemporaryRasterFile(dst_path))
                        tmp_dst = ctx.enter_context(
                            rasterio.open(tmpfile.name, "w", **meta))

                    wind = list(tmp_dst.block_windows(1))

                    if not quiet:
                        click.echo("Reading input: {}".format(src_path),
                                   err=True)
                    fout = os.devnull if quiet else sys.stderr
                    with click.progressbar(wind,
                                           length=len(wind),
                                           file=fout,
                                           show_percent=True) as windows:
                        for ij, w in windows:
                            matrix = vrt_dst.read(window=w, indexes=indexes)
                            tmp_dst.write(matrix, window=w)

                            if add_mask or mask:
                                mask_value = vrt_dst.dataset_mask(window=w)
                                tmp_dst.write_mask(mask_value, window=w)

                    if overview_level is None:
                        overview_level = get_maximum_overview_level(
                            vrt_dst, tilesize)

                    if not quiet and overview_level:
                        click.echo("Adding overviews...", err=True)

                    overviews = [2**j for j in range(1, overview_level + 1)]
                    tmp_dst.build_overviews(
                        overviews, ResamplingEnums[overview_resampling])

                    if not quiet:
                        click.echo("Updating dataset tags...", err=True)

                    for i, b in enumerate(indexes):
                        tmp_dst.set_band_description(
                            i + 1, src_dst.descriptions[b - 1])

                    tags = src_dst.tags()
                    tags.update(
                        dict(OVR_RESAMPLING_ALG=ResamplingEnums[
                            overview_resampling].name.upper()))
                    tmp_dst.update_tags(**tags)

                    if not quiet:
                        click.echo("Writing output to: {}".format(dst_path),
                                   err=True)
                    copy(tmp_dst,
                         dst_path,
                         copy_src_overviews=True,
                         **dst_kwargs)