diff --git a/CHANGES.md b/CHANGES.md index 1c07d00..9a1843c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,14 @@ * fix COG validation for SPARSE dataset (author @mpadillaruiz, https://github.com/cogeotiff/rio-cogeo/issues/281) +#### CLI + +* remove default (*128*) for `--overview-blocksize` option in the CLI. Now defaults to GDAL behaviour. + +* change how `blocksize` for overviews is defined when using `tms` or `web-optimized` options + +* `blocksize` is now defined from the tilematrixset's `tileWidth` and `tileHeight` when `--blocksize` is not provided + ## 5.1.1 (2024-01-08) * use morecantile `TileMatrixSet.cellSize` property instead of deprecated/private `TileMatrixSet._resolution` method diff --git a/rio_cogeo/scripts/cli.py b/rio_cogeo/scripts/cli.py index df38454..927bf25 100644 --- a/rio_cogeo/scripts/cli.py +++ b/rio_cogeo/scripts/cli.py @@ -131,10 +131,8 @@ def cogeo(): ) @click.option( "--overview-blocksize", - default=lambda: os.environ.get("GDAL_TIFF_OVR_BLOCKSIZE", 128), - help="Overview's internal tile size (default defined by " - "GDAL_TIFF_OVR_BLOCKSIZE env or 128)", - show_default=True, + help="Overview's internal tile size (default can be defined by " + "GDAL_TIFF_OVR_BLOCKSIZE env). The default is 128, or starting with GDAL 3.1 to use the same block size as the full-resolution dataset if possible).", ) @click.option( "--web-optimized", "-w", is_flag=True, help="Create COGEO optimized for Web." @@ -251,26 +249,18 @@ def create( quiet, ): """Create Cloud Optimized Geotiff.""" - output_profile = cog_profiles.get(cogeo_profile) - output_profile.update({"BIGTIFF": os.environ.get("BIGTIFF", "IF_SAFER")}) - if creation_options: - output_profile.update(creation_options) - - if blocksize: - output_profile["blockxsize"] = blocksize - output_profile["blockysize"] = blocksize - - if web_optimized: - overview_blocksize = blocksize or 512 - config.update( { "GDAL_NUM_THREADS": threads, "GDAL_TIFF_INTERNAL_MASK": os.environ.get("GDAL_TIFF_INTERNAL_MASK", True), - "GDAL_TIFF_OVR_BLOCKSIZE": str(overview_blocksize), } ) + output_profile = cog_profiles.get(cogeo_profile) + output_profile.update({"BIGTIFF": os.environ.get("BIGTIFF", "IF_SAFER")}) + if creation_options: + output_profile.update(creation_options) + tilematrixset: Optional[TileMatrixSet] = None if tms: with open(tms, "r") as f: @@ -279,6 +269,33 @@ def create( elif web_optimized: tilematrixset = morecantile.tms.get("WebMercatorQuad") + if tilematrixset: + if not blocksize: + click.secho( + f"Defining `blocksize` from {tilematrixset.id} TileMatrixSet `tileWidth` and `tileHeight`", + fg="yellow", + ) + + blocksize = min( + tilematrixset.matrix(tilematrixset.minzoom).tileHeight, + tilematrixset.matrix(tilematrixset.minzoom).tileWidth, + ) + + if not overview_blocksize: + click.secho( + f"Defining overview's `blocksize` to match the high resolution `blocksize`: {blocksize}", + fg="yellow", + ) + overview_blocksize = blocksize + + if blocksize: + output_profile["blockxsize"] = blocksize + output_profile["blockysize"] = blocksize + + overview_blocksize = overview_blocksize or os.environ.get("GDAL_TIFF_OVR_BLOCKSIZE") + if overview_blocksize: + config.update({"GDAL_TIFF_OVR_BLOCKSIZE": str(overview_blocksize)}) + cog_translate( input, output, diff --git a/tests/test_cli.py b/tests/test_cli.py index 873007b..0bdd90d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,6 +2,7 @@ import os +import morecantile import pytest import rasterio @@ -513,30 +514,23 @@ def test_cogeo_validUpercaseProfile(monkeypatch, runner): assert src.compression.value == "DEFLATE" -def test_cogeo_info(runner): +@pytest.mark.parametrize( + "src_path", + [ + raster_path_rgb, + raster_invalid_cog, + raster_band_tags, + raster_path_gcps, + ], +) +def test_cogeo_info(src_path, runner): """Should work as expected.""" with runner.isolated_filesystem(): - result = runner.invoke(cogeo, ["info", raster_path_rgb]) - assert not result.exception - assert result.exit_code == 0 - - result = runner.invoke(cogeo, ["info", raster_path_rgb, "--json"]) - assert not result.exception - assert result.exit_code == 0 - - result = runner.invoke(cogeo, ["info", raster_invalid_cog]) - assert not result.exception - assert result.exit_code == 0 - - result = runner.invoke(cogeo, ["info", raster_path_gcps]) + result = runner.invoke(cogeo, ["info", src_path]) assert not result.exception assert result.exit_code == 0 - result = runner.invoke(cogeo, ["info", raster_band_tags, "--json"]) - assert not result.exception - assert result.exit_code == 0 - - result = runner.invoke(cogeo, ["info", raster_band_tags]) + result = runner.invoke(cogeo, ["info", src_path, "--json"]) assert not result.exception assert result.exit_code == 0 @@ -577,3 +571,104 @@ def test_cogeo_zoom_level(runner): with rasterio.open("output.tif") as src: _, max_zoom = get_zooms(src) assert max_zoom == 19 + + +def test_create_web_tms(runner): + """Should work as expected.""" + with runner.isolated_filesystem(): + result = runner.invoke( + cogeo, + [ + "create", + raster_path_rgb, + "output.tif", + "--web-optimized", + "--aligned-levels", + 2, + ], + ) + assert not result.exception + assert result.exit_code == 0 + with rasterio.open("output.tif") as src: + meta = src.profile + + with rasterio.open("output.tif", OVERVIEW_LEVEL=0) as src: + meta_ovr = src.profile + assert meta_ovr["blockysize"] == meta["blockysize"] + + with open("webmercator.json", "w") as f: + tms = morecantile.tms.get("WebMercatorQuad") + f.write(tms.model_dump_json()) + + result = runner.invoke( + cogeo, + [ + "create", + raster_path_rgb, + "output.tif", + "--aligned-levels", + "2", + "--tms", + "webmercator.json", + ], + ) + assert not result.exception + assert result.exit_code == 0 + with rasterio.open("output.tif") as src: + meta_tms = src.profile + + assert meta_tms["crs"] == meta["crs"] + assert meta_tms["transform"] == meta["transform"] + assert meta_tms["blockysize"] == meta["blockysize"] == 256 + assert meta_tms["width"] == meta["width"] + assert meta_tms["height"] == meta["height"] + + with rasterio.open("output.tif", OVERVIEW_LEVEL=0) as src: + meta_ovr = src.profile + assert meta_ovr["blockysize"] == meta_tms["blockysize"] == 256 + + result = runner.invoke( + cogeo, + [ + "create", + raster_path_rgb, + "output.tif", + "--aligned-levels", + "2", + "--blocksize", + 512, + "--tms", + "webmercator.json", + ], + ) + assert not result.exception + assert result.exit_code == 0 + with rasterio.open("output.tif") as src: + meta_tms = src.profile + + with rasterio.open("output.tif", OVERVIEW_LEVEL=0) as src: + meta_ovr = src.profile + assert meta_ovr["blockysize"] == meta_tms["blockysize"] == 512 + + result = runner.invoke( + cogeo, + [ + "create", + raster_path_rgb, + "output.tif", + "--aligned-levels", + "2", + "--blocksize", + 512, + "--overview-blocksize", + 128, + "--tms", + "webmercator.json", + ], + ) + assert not result.exception + assert result.exit_code == 0 + + with rasterio.open("output.tif", OVERVIEW_LEVEL=0) as src: + meta_ovr = src.profile + assert meta_ovr["blockysize"] == 128