1726 строки
55 KiB
Python
Исполняемый файл
1726 строки
55 KiB
Python
Исполняемый файл
#!/usr/bin/env pytest
|
|
# -*- coding: utf-8 -*-
|
|
###############################################################################
|
|
# $Id$
|
|
#
|
|
# Project: GDAL/OGR Test Suite
|
|
# Purpose: COG driver testing
|
|
# Author: Even Rouault <even.rouault at spatialys.com>
|
|
#
|
|
###############################################################################
|
|
# Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
# to deal in the Software without restriction, including without limitation
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included
|
|
# in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
# DEALINGS IN THE SOFTWARE.
|
|
###############################################################################
|
|
|
|
import struct
|
|
import sys
|
|
|
|
import gdaltest
|
|
import pytest
|
|
from test_py_scripts import samples_path
|
|
|
|
from osgeo import gdal, osr
|
|
|
|
|
|
###############################################################################
|
|
@pytest.fixture(autouse=True, scope="module")
|
|
def module_disable_exceptions():
|
|
with gdaltest.disable_exceptions():
|
|
yield
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def _check_cog(filename):
|
|
|
|
path = samples_path
|
|
if path not in sys.path:
|
|
sys.path.append(path)
|
|
import validate_cloud_optimized_geotiff
|
|
|
|
try:
|
|
_, errors, _ = validate_cloud_optimized_geotiff.validate(
|
|
filename, full_check=True
|
|
)
|
|
assert not errors, "validate_cloud_optimized_geotiff failed"
|
|
except OSError:
|
|
pytest.fail("validate_cloud_optimized_geotiff failed")
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def check_libtiff_internal_or_at_least(expected_maj, expected_min, expected_micro):
|
|
|
|
md = gdal.GetDriverByName("GTiff").GetMetadata()
|
|
if md["LIBTIFF"] == "INTERNAL":
|
|
return True
|
|
if md["LIBTIFF"].startswith("LIBTIFF, Version "):
|
|
version = md["LIBTIFF"][len("LIBTIFF, Version ") :]
|
|
version = version[0 : version.find("\n")]
|
|
got_maj, got_min, got_micro = version.split(".")
|
|
got_maj = int(got_maj)
|
|
got_min = int(got_min)
|
|
got_micro = int(got_micro)
|
|
if got_maj > expected_maj:
|
|
return True
|
|
if got_maj < expected_maj:
|
|
return False
|
|
if got_min > expected_min:
|
|
return True
|
|
if got_min < expected_min:
|
|
return False
|
|
return got_micro >= expected_micro
|
|
return False
|
|
|
|
|
|
###############################################################################
|
|
# Basic test
|
|
|
|
|
|
def test_cog_basic():
|
|
|
|
tab = [0]
|
|
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
assert src_ds.GetMetadataItem("GDAL_STRUCTURAL_METADATA", "TIFF") is None
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, callback=my_cbk, callback_data=tab
|
|
)
|
|
src_ds = None
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == 4672
|
|
assert ds.GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE") == "COG"
|
|
assert ds.GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE") == "LZW"
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 0
|
|
assert ds.GetRasterBand(1).GetBlockSize() == [512, 512]
|
|
assert (
|
|
ds.GetMetadataItem("GDAL_STRUCTURAL_METADATA", "TIFF")
|
|
== """GDAL_STRUCTURAL_METADATA_SIZE=000140 bytes
|
|
LAYOUT=IFDS_BEFORE_DATA
|
|
BLOCK_ORDER=ROW_MAJOR
|
|
BLOCK_LEADER=SIZE_AS_UINT4
|
|
BLOCK_TRAILER=LAST_4_BYTES_REPEATED
|
|
KNOWN_INCOMPATIBLE_EDITION=NO
|
|
"""
|
|
)
|
|
ds = None
|
|
_check_cog(filename)
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test creation options
|
|
|
|
|
|
def test_cog_creation_options():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE", "LEVEL=1", "NUM_THREADS=2"]
|
|
)
|
|
assert ds
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == 4672
|
|
assert ds.GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE") == "DEFLATE"
|
|
assert ds.GetMetadataItem("PREDICTOR", "IMAGE_STRUCTURE") is None
|
|
ds = None
|
|
filesize = gdal.VSIStatL(filename).size
|
|
_check_cog(filename)
|
|
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE", "BIGTIFF=YES", "LEVEL=1"]
|
|
)
|
|
assert gdal.VSIStatL(filename).size != filesize
|
|
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE", "PREDICTOR=YES", "LEVEL=1"]
|
|
)
|
|
assert gdal.VSIStatL(filename).size != filesize
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetMetadataItem("PREDICTOR", "IMAGE_STRUCTURE") == "2"
|
|
ds = None
|
|
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE", "LEVEL=9"]
|
|
)
|
|
assert gdal.VSIStatL(filename).size < filesize
|
|
|
|
colist = gdal.GetDriverByName("COG").GetMetadataItem("DMD_CREATIONOPTIONLIST")
|
|
if "<Value>ZSTD" in colist:
|
|
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=ZSTD"]
|
|
)
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE") == "ZSTD"
|
|
ds = None
|
|
|
|
if "<Value>LZMA" in colist:
|
|
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LZMA"]
|
|
)
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE") == "LZMA"
|
|
ds = None
|
|
|
|
if "<Value>WEBP" in colist:
|
|
|
|
with gdaltest.error_handler():
|
|
assert not gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=WEBP"]
|
|
)
|
|
|
|
if "<Value>LERC" in colist:
|
|
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC"]
|
|
)
|
|
filesize_no_z_error = gdal.VSIStatL(filename).size
|
|
assert gdal.VSIStatL(filename).size != filesize
|
|
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC", "MAX_Z_ERROR=10"]
|
|
)
|
|
filesize_with_z_error = gdal.VSIStatL(filename).size
|
|
assert filesize_with_z_error < filesize_no_z_error
|
|
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC_DEFLATE"]
|
|
)
|
|
filesize_lerc_deflate = gdal.VSIStatL(filename).size
|
|
assert filesize_lerc_deflate < filesize_no_z_error
|
|
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC_DEFLATE", "LEVEL=1"]
|
|
)
|
|
filesize_lerc_deflate_level_1 = gdal.VSIStatL(filename).size
|
|
assert filesize_lerc_deflate_level_1 > filesize_lerc_deflate
|
|
|
|
if "<Value>ZSTD" in colist:
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC_ZSTD"]
|
|
)
|
|
filesize_lerc_zstd = gdal.VSIStatL(filename).size
|
|
assert filesize_lerc_zstd < filesize_no_z_error
|
|
|
|
assert gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=LERC_ZSTD", "LEVEL=1"]
|
|
)
|
|
filesize_lerc_zstd_level_1 = gdal.VSIStatL(filename).size
|
|
assert filesize_lerc_zstd_level_1 > filesize_lerc_zstd
|
|
|
|
src_ds = None
|
|
with gdaltest.error_handler():
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test creation of overviews
|
|
|
|
|
|
def test_cog_creation_of_overviews():
|
|
|
|
tab = [0]
|
|
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
directory = "/vsimem/test_cog_creation_of_overviews"
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Translate("", "data/byte.tif", options="-of MEM -outsize 2048 300")
|
|
|
|
with gdaltest.config_option("GDAL_TIFF_INTERNAL_MASK", "YES"):
|
|
check_filename = "/vsimem/tmp.tif"
|
|
ds = gdal.GetDriverByName("GTiff").CreateCopy(
|
|
check_filename, src_ds, options=["TILED=YES"]
|
|
)
|
|
ds.BuildOverviews("CUBIC", [2, 4])
|
|
cs1 = ds.GetRasterBand(1).GetOverview(0).Checksum()
|
|
cs2 = ds.GetRasterBand(1).GetOverview(1).Checksum()
|
|
ds = None
|
|
gdal.Unlink(check_filename)
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, callback=my_cbk, callback_data=tab
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
assert len(gdal.ReadDir(directory)) == 1 # check that the temp file has gone away
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() == cs1
|
|
assert ds.GetRasterBand(1).GetOverview(1).Checksum() == cs2
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
src_ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test creation of overviews with a different compression method
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_creation_of_overviews_with_compression():
|
|
|
|
directory = "/vsimem/test_cog_creation_of_overviews_with_compression"
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Translate("", "data/byte.tif", options="-of MEM -outsize 2048 300")
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["COMPRESS=LZW", "OVERVIEW_COMPRESS=JPEG", "OVERVIEW_QUALITY=50"],
|
|
)
|
|
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
assert ds.GetMetadata("IMAGE_STRUCTURE")["COMPRESSION"] == "LZW"
|
|
|
|
ds_overview_a = gdal.Open("GTIFF_DIR:2:" + filename)
|
|
assert ds_overview_a.GetMetadata("IMAGE_STRUCTURE")["COMPRESSION"] == "JPEG"
|
|
assert ds_overview_a.GetMetadata("IMAGE_STRUCTURE")["JPEG_QUALITY"] == "50"
|
|
|
|
ds_overview_b = gdal.Open("GTIFF_DIR:3:" + filename)
|
|
assert ds_overview_b.GetMetadata("IMAGE_STRUCTURE")["COMPRESSION"] == "JPEG"
|
|
assert ds_overview_a.GetMetadata("IMAGE_STRUCTURE")["JPEG_QUALITY"] == "50"
|
|
|
|
ds_overview_a = None
|
|
ds_overview_b = None
|
|
ds = None
|
|
|
|
src_ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test creation of overviews with a dataset with a mask
|
|
|
|
|
|
def test_cog_creation_of_overviews_with_mask():
|
|
|
|
tab = [0]
|
|
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
directory = "/vsimem/test_cog_creation_of_overviews_with_mask"
|
|
gdal.Mkdir(directory, 0o755)
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Translate("", "data/byte.tif", options="-of MEM -outsize 2048 300")
|
|
src_ds.CreateMaskBand(gdal.GMF_PER_DATASET)
|
|
src_ds.GetRasterBand(1).GetMaskBand().WriteRaster(
|
|
0, 0, 1024, 300, b"\xFF", buf_xsize=1, buf_ysize=1
|
|
)
|
|
|
|
with gdaltest.config_option("GDAL_TIFF_INTERNAL_MASK", "YES"):
|
|
check_filename = "/vsimem/tmp.tif"
|
|
ds = gdal.GetDriverByName("GTiff").CreateCopy(
|
|
check_filename, src_ds, options=["TILED=YES"]
|
|
)
|
|
ds.BuildOverviews("CUBIC", [2, 4])
|
|
cs1 = ds.GetRasterBand(1).GetOverview(0).Checksum()
|
|
cs2 = ds.GetRasterBand(1).GetOverview(1).Checksum()
|
|
ds = None
|
|
gdal.Unlink(check_filename)
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, callback=my_cbk, callback_data=tab
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
assert len(gdal.ReadDir(directory)) == 1 # check that the temp file has gone away
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
assert ds.GetRasterBand(1).GetOverview(0).GetBlockSize() == [512, 512]
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() == cs1
|
|
assert ds.GetRasterBand(1).GetOverview(1).Checksum() == cs2
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
src_ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test full world reprojection to WebMercator
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_small_world_to_web_mercator():
|
|
|
|
tab = [0]
|
|
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
directory = "/vsimem/test_cog_small_world_to_web_mercator"
|
|
gdal.Mkdir(directory, 0o755)
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Open("../gdrivers/data/small_world.tif")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "COMPRESS=JPEG"],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
assert len(gdal.ReadDir(directory)) == 1 # check that the temp file has gone away
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.RasterCount == 3
|
|
assert ds.RasterXSize == 256
|
|
assert ds.RasterYSize == 256
|
|
assert ds.GetRasterBand(1).GetMaskFlags() == gdal.GMF_PER_DATASET
|
|
assert ds.GetRasterBand(1).GetBlockSize() == [256, 256]
|
|
gt = ds.GetGeoTransform()
|
|
assert gt[1] == -gt[5] # yes, checking for strict equality
|
|
expected_gt = [
|
|
-20037508.342789248,
|
|
156543.033928041,
|
|
0.0,
|
|
20037508.342789248,
|
|
0.0,
|
|
-156543.033928041,
|
|
]
|
|
for i in range(6):
|
|
if gt[i] != pytest.approx(expected_gt[i], abs=1e-10 * abs(expected_gt[i])):
|
|
assert False, gt
|
|
got_cs = [ds.GetRasterBand(i + 1).Checksum() for i in range(3)]
|
|
assert got_cs in (
|
|
[26293, 23439, 14955],
|
|
[26228, 22085, 12992],
|
|
[25088, 23140, 13265], # libjpeg 9e
|
|
)
|
|
assert ds.GetRasterBand(1).GetMaskBand().Checksum() == 17849
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
src_ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test reprojection of small extent to WebMercator
|
|
|
|
|
|
def test_cog_byte_to_web_mercator():
|
|
|
|
tab = [0]
|
|
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
directory = "/vsimem/test_cog_byte_to_web_mercator"
|
|
gdal.Mkdir(directory, 0o755)
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "ALIGNED_LEVELS=3"],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
assert len(gdal.ReadDir(directory)) == 1 # check that the temp file has gone away
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.RasterCount == 2
|
|
assert ds.RasterXSize == 1024
|
|
assert ds.RasterYSize == 1024
|
|
assert ds.GetRasterBand(1).GetMaskFlags() == gdal.GMF_ALPHA + gdal.GMF_PER_DATASET
|
|
assert ds.GetRasterBand(1).GetBlockSize() == [256, 256]
|
|
gt = ds.GetGeoTransform()
|
|
assert gt[1] == -gt[5] # yes, checking for strict equality
|
|
expected_gt = [
|
|
-13149614.849955443,
|
|
76.43702828517598,
|
|
0.0,
|
|
4070118.8821290657,
|
|
0.0,
|
|
-76.43702828517598,
|
|
]
|
|
for i in range(6):
|
|
if gt[i] != pytest.approx(expected_gt[i], abs=1e-10 * abs(expected_gt[i])):
|
|
assert False, gt
|
|
assert ds.GetRasterBand(1).Checksum() in (
|
|
4363,
|
|
4264, # got on Mac at some point
|
|
4362, # libjpeg 9d
|
|
4569, # libjpeg 9e
|
|
)
|
|
assert ds.GetRasterBand(1).GetMaskBand().Checksum() == 4356
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
# Use our generated COG as the input of the same COG generation: reprojection
|
|
# should be skipped
|
|
filename2 = directory + "/cog2.tif"
|
|
src_ds = gdal.Open(filename)
|
|
|
|
class my_error_handler(object):
|
|
def __init__(self):
|
|
self.debug_msg_list = []
|
|
self.other_msg_list = []
|
|
|
|
def handler(self, eErrClass, err_no, msg):
|
|
if eErrClass == gdal.CE_Debug:
|
|
self.debug_msg_list.append(msg)
|
|
else:
|
|
self.other_msg_list.append(msg)
|
|
|
|
handler = my_error_handler()
|
|
try:
|
|
gdal.PushErrorHandler(handler.handler)
|
|
gdal.SetCurrentErrorHandlerCatchDebug(True)
|
|
with gdaltest.config_option("CPL_DEBUG", "COG"):
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename2,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "ALIGNED_LEVELS=3"],
|
|
)
|
|
finally:
|
|
gdal.PopErrorHandler()
|
|
|
|
assert ds
|
|
assert (
|
|
"COG: Skipping reprojection step: source dataset matches reprojection specifications"
|
|
in handler.debug_msg_list
|
|
)
|
|
assert handler.other_msg_list == []
|
|
src_ds = None
|
|
ds = None
|
|
|
|
# Cleanup
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.GetDriverByName("GTiff").Delete(filename2)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Same as previous test case but with other input options
|
|
|
|
|
|
def test_cog_byte_to_web_mercator_manual():
|
|
|
|
directory = "/vsimem/test_cog_byte_to_web_mercator_manual"
|
|
gdal.Mkdir(directory, 0o755)
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
res = 76.43702828517598
|
|
minx = -13149614.849955443
|
|
maxx = minx + 1024 * res
|
|
maxy = 4070118.8821290657
|
|
miny = maxy - 1024 * res
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"BLOCKSIZE=256",
|
|
"TARGET_SRS=EPSG:3857",
|
|
"RES=%.18g" % res,
|
|
"EXTENT=%.18g,%.18g,%.18g,%.18g" % (minx, miny, maxx, maxy),
|
|
],
|
|
)
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.RasterCount == 2
|
|
assert ds.RasterXSize == 1024
|
|
assert ds.RasterYSize == 1024
|
|
assert ds.GetRasterBand(1).GetMaskFlags() == gdal.GMF_ALPHA + gdal.GMF_PER_DATASET
|
|
assert ds.GetRasterBand(1).GetBlockSize() == [256, 256]
|
|
expected_gt = [
|
|
-13149614.849955443,
|
|
76.43702828517598,
|
|
0.0,
|
|
4070118.8821290657,
|
|
0.0,
|
|
-76.43702828517598,
|
|
]
|
|
assert ds.GetGeoTransform() == pytest.approx(expected_gt, rel=1e-10)
|
|
assert ds.GetRasterBand(1).Checksum() in (
|
|
4363,
|
|
4264, # got on Mac at some point
|
|
4362, # libjpeg 9d
|
|
4569, # libjpeg 9e
|
|
)
|
|
assert ds.GetRasterBand(1).GetMaskBand().Checksum() == 4356
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
ds = None
|
|
src_ds = None
|
|
|
|
# Check that we correctly round to the closest tile if input bounds are
|
|
# very close to its boundary (less than half a pixel)
|
|
filename2 = directory + "/cog2.tif"
|
|
eps = 0.49 * res
|
|
gdal.Translate(
|
|
filename2,
|
|
filename,
|
|
options="-of COG -co TILING_SCHEME=GoogleMapsCompatible -a_ullr %.18g %.18g %.18g %.18g"
|
|
% (minx - eps, maxy + eps, maxx + eps, miny - eps),
|
|
)
|
|
ds = gdal.Open(filename2)
|
|
assert ds.RasterXSize == 1024
|
|
assert ds.RasterYSize == 1024
|
|
assert ds.GetGeoTransform() == pytest.approx(expected_gt, rel=1e-10)
|
|
ds = None
|
|
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.GetDriverByName("GTiff").Delete(filename2)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test OVERVIEWS creation option
|
|
|
|
|
|
def test_cog_overviews_co():
|
|
def my_cbk(pct, _, arg):
|
|
assert pct >= tab[0]
|
|
tab[0] = pct
|
|
return 1
|
|
|
|
directory = "/vsimem/test_cog_overviews_co"
|
|
filename = directory + "/cog.tif"
|
|
src_ds = gdal.Translate("", "data/byte.tif", options="-of MEM -outsize 2048 300")
|
|
|
|
for val in ["NONE", "FORCE_USE_EXISTING"]:
|
|
|
|
tab = [0]
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["OVERVIEWS=" + val],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
for val in ["AUTO", "IGNORE_EXISTING"]:
|
|
|
|
tab = [0]
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["OVERVIEWS=" + val],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() != 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
# Add overviews to source
|
|
src_ds.BuildOverviews("NONE", [2])
|
|
|
|
tab = [0]
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["OVERVIEWS=NONE"], callback=my_cbk, callback_data=tab
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
tab = [0]
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["OVERVIEWS=FORCE_USE_EXISTING"],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 1
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() == 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
tab = [0]
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["OVERVIEWS=IGNORE_EXISTING"],
|
|
callback=my_cbk,
|
|
callback_data=tab,
|
|
)
|
|
assert tab[0] == 1.0
|
|
assert ds
|
|
|
|
ds = None
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 2
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() != 0
|
|
ds = None
|
|
_check_cog(filename)
|
|
|
|
src_ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
gdal.Unlink(directory)
|
|
|
|
|
|
###############################################################################
|
|
# Test editing and invalidating a COG file
|
|
|
|
|
|
def test_cog_invalidation_by_data_change():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 100, 100)
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE"]
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.Open(filename, gdal.GA_Update)
|
|
assert ds.GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE") == "COG"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
data = src_ds.ReadRaster()
|
|
ds.GetRasterBand(1).WriteRaster(0, 0, 20, 20, data)
|
|
with gdaltest.error_handler():
|
|
assert ds.FlushCache() == gdal.CE_None
|
|
ds = None
|
|
|
|
with gdaltest.error_handler():
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE") is None
|
|
ds = None
|
|
|
|
with pytest.raises(
|
|
AssertionError, match="KNOWN_INCOMPATIBLE_EDITION=YES is declared in the file"
|
|
):
|
|
_check_cog(filename)
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test editing and invalidating a COG file
|
|
|
|
|
|
def test_cog_invalidation_by_metadata_change():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1)
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["COMPRESS=DEFLATE"]
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.Open(filename, gdal.GA_Update)
|
|
ds.GetRasterBand(1).ComputeStatistics(False)
|
|
ds = None
|
|
|
|
with gdaltest.error_handler():
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE") is None
|
|
ds = None
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test a tiling scheme with a CRS with northing/easting axis order
|
|
# and non power-of-two ratios of scales.
|
|
|
|
|
|
def test_cog_northing_easting_and_non_power_of_two_ratios():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
|
|
x0_NZTM2000 = -1000000
|
|
y0_NZTM2000 = 10000000
|
|
blocksize = 256
|
|
scale_denom_zoom_level_14 = 1000
|
|
scale_denom_zoom_level_13 = 2500
|
|
scale_denom_zoom_level_12 = 5000
|
|
|
|
ds = gdal.Translate(
|
|
filename,
|
|
"data/byte.tif",
|
|
options="-of COG -a_srs EPSG:2193 -a_ullr 1000001 5000001 1000006.6 4999995.4 -co TILING_SCHEME=NZTM2000 -co ALIGNED_LEVELS=2",
|
|
)
|
|
assert ds.RasterXSize == 1280
|
|
assert ds.RasterYSize == 1280
|
|
b = ds.GetRasterBand(1)
|
|
assert [
|
|
(b.GetOverview(i).XSize, b.GetOverview(i).YSize)
|
|
for i in range(b.GetOverviewCount())
|
|
] == [(512, 512), (256, 256)]
|
|
|
|
gt = ds.GetGeoTransform()
|
|
assert gt[1] == -gt[5] # yes, checking for strict equality
|
|
|
|
res_zoom_level_14 = (
|
|
scale_denom_zoom_level_14 * 0.28e-3
|
|
) # According to OGC Tile Matrix Set formula
|
|
assert gt == pytest.approx(
|
|
(999872, res_zoom_level_14, 0, 5000320, 0, -res_zoom_level_14), abs=1e-8
|
|
)
|
|
|
|
# Check that gt origin matches the corner of a tile at zoom 14
|
|
res = gt[1]
|
|
tile_x = (gt[0] - x0_NZTM2000) / (blocksize * res)
|
|
assert tile_x == pytest.approx(round(tile_x))
|
|
tile_y = (y0_NZTM2000 - gt[3]) / (blocksize * res)
|
|
assert tile_y == pytest.approx(round(tile_y))
|
|
|
|
# Check that overview=0 corresponds to the resolution of zoom level=13 / OGC ScaleDenom = 2500
|
|
ovr0_xsize = b.GetOverview(0).XSize
|
|
assert (
|
|
float(ovr0_xsize) / ds.RasterXSize
|
|
== float(scale_denom_zoom_level_14) / scale_denom_zoom_level_13
|
|
)
|
|
# Check that gt origin matches the corner of a tile at zoom 13
|
|
ovr0_res = res * scale_denom_zoom_level_13 / scale_denom_zoom_level_14
|
|
tile_x = (gt[0] - x0_NZTM2000) / (blocksize * ovr0_res)
|
|
assert tile_x == pytest.approx(round(tile_x))
|
|
tile_y = (y0_NZTM2000 - gt[3]) / (blocksize * ovr0_res)
|
|
assert tile_y == pytest.approx(round(tile_y))
|
|
|
|
# Check that overview=1 corresponds to the resolution of zoom level=12 / OGC ScaleDenom = 5000
|
|
ovr1_xsize = b.GetOverview(1).XSize
|
|
assert (
|
|
float(ovr1_xsize) / ds.RasterXSize
|
|
== float(scale_denom_zoom_level_14) / scale_denom_zoom_level_12
|
|
)
|
|
# Check that gt origin matches the corner of a tile at zoom 12
|
|
ovr1_res = res * scale_denom_zoom_level_12 / scale_denom_zoom_level_14
|
|
tile_x = (gt[0] - x0_NZTM2000) / (blocksize * ovr1_res)
|
|
assert tile_x == pytest.approx(round(tile_x))
|
|
tile_y = (y0_NZTM2000 - gt[3]) / (blocksize * ovr1_res)
|
|
assert tile_y == pytest.approx(round(tile_y))
|
|
|
|
assert ds.GetMetadata("TILING_SCHEME") == {
|
|
"NAME": "NZTM2000",
|
|
"ZOOM_LEVEL": "14",
|
|
"ALIGNED_LEVELS": "2",
|
|
}
|
|
|
|
ds = None
|
|
gdal.GetDriverByName("GTiff").Delete(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SPARSE_OK=YES
|
|
|
|
|
|
def test_cog_sparse():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512)
|
|
src_ds.GetRasterBand(1).Fill(255)
|
|
src_ds.WriteRaster(0, 0, 256, 256, "\x00" * 256 * 256)
|
|
src_ds.WriteRaster(256, 256, 128, 128, "\x00" * 128 * 128)
|
|
src_ds.BuildOverviews("NEAREST", [2])
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=128", "SPARSE_OK=YES", "COMPRESS=LZW"]
|
|
)
|
|
_check_cog(filename)
|
|
with gdaltest.config_option("GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "YES"):
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF") is None
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF") is None
|
|
assert (
|
|
ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_2_0", "TIFF") is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512) == src_ds.GetRasterBand(
|
|
1
|
|
).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetOverview(0).ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
|
|
if check_libtiff_internal_or_at_least(4, 0, 11):
|
|
# This file is the same as the one generated above, except that we have,
|
|
# with an hex editor, zeroify all entries of TileByteCounts except the
|
|
# last tile of the main IFD, and for a tile when the next tile is sparse
|
|
ds = gdal.Open("data/cog_sparse_strile_arrays_zeroified_when_possible.tif")
|
|
assert ds.GetRasterBand(1).ReadRaster(
|
|
0, 0, 512, 512
|
|
) == src_ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SPARSE_OK=YES with mask
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_sparse_mask():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512, 4)
|
|
for i in range(4):
|
|
src_ds.GetRasterBand(i + 1).SetColorInterpretation(gdal.GCI_RedBand + i)
|
|
src_ds.GetRasterBand(i + 1).Fill(255)
|
|
src_ds.GetRasterBand(i + 1).WriteRaster(0, 0, 256, 256, "\x00" * 256 * 256)
|
|
src_ds.GetRasterBand(i + 1).WriteRaster(256, 256, 128, 128, "\x00" * 128 * 128)
|
|
src_ds.BuildOverviews("NEAREST", [2])
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"BLOCKSIZE=128",
|
|
"SPARSE_OK=YES",
|
|
"COMPRESS=JPEG",
|
|
"RESAMPLING=NEAREST",
|
|
],
|
|
)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
_check_cog(filename)
|
|
with gdaltest.config_option("GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "YES"):
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF") is None
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF") is None
|
|
assert (
|
|
ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_2_0", "TIFF") is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_2_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_1_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512) == src_ds.GetRasterBand(
|
|
1
|
|
).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetMaskBand().ReadRaster(
|
|
0, 0, 512, 512
|
|
) == src_ds.GetRasterBand(4).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetOverview(0).ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
assert ds.GetRasterBand(1).GetOverview(0).GetMaskBand().ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(4).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SPARSE_OK=YES with imagery at 0 and mask at 255
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_sparse_imagery_0_mask_255():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512, 4)
|
|
for i in range(4):
|
|
src_ds.GetRasterBand(i + 1).SetColorInterpretation(gdal.GCI_RedBand + i)
|
|
src_ds.GetRasterBand(i + 1).Fill(0 if i < 3 else 255)
|
|
src_ds.BuildOverviews("NEAREST", [2])
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=128", "SPARSE_OK=YES", "COMPRESS=JPEG"]
|
|
)
|
|
_check_cog(filename)
|
|
with gdaltest.config_option("GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "YES"):
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF") is None
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512) == src_ds.GetRasterBand(
|
|
1
|
|
).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetMaskBand().ReadRaster(
|
|
0, 0, 512, 512
|
|
) == src_ds.GetRasterBand(4).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetOverview(0).ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
assert ds.GetRasterBand(1).GetOverview(0).GetMaskBand().ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(4).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SPARSE_OK=YES with imagery at 0 or 255 and mask at 255
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_sparse_imagery_0_or_255_mask_255():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512, 4)
|
|
for i in range(4):
|
|
src_ds.GetRasterBand(i + 1).SetColorInterpretation(gdal.GCI_RedBand + i)
|
|
for i in range(3):
|
|
src_ds.GetRasterBand(i + 1).Fill(255)
|
|
src_ds.GetRasterBand(i + 1).WriteRaster(0, 0, 256, 256, "\x00" * 256 * 256)
|
|
src_ds.GetRasterBand(i + 1).WriteRaster(256, 256, 128, 128, "\x00" * 128 * 128)
|
|
src_ds.GetRasterBand(4).Fill(255)
|
|
src_ds.BuildOverviews("NEAREST", [2])
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"BLOCKSIZE=128",
|
|
"SPARSE_OK=YES",
|
|
"COMPRESS=JPEG",
|
|
"RESAMPLING=NEAREST",
|
|
],
|
|
)
|
|
_check_cog(filename)
|
|
with gdaltest.config_option("GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "YES"):
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF") is None
|
|
assert (
|
|
ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_2_0", "TIFF") is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is not None
|
|
)
|
|
assert ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512) == src_ds.GetRasterBand(
|
|
1
|
|
).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetMaskBand().ReadRaster(
|
|
0, 0, 512, 512
|
|
) == src_ds.GetRasterBand(4).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetOverview(0).ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
assert ds.GetRasterBand(1).GetOverview(0).GetMaskBand().ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(4).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SPARSE_OK=YES with imagery and mask at 0
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JPEG")
|
|
def test_cog_sparse_imagery_mask_0():
|
|
|
|
filename = "/vsimem/cog.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512, 4)
|
|
for i in range(4):
|
|
src_ds.GetRasterBand(i + 1).SetColorInterpretation(gdal.GCI_RedBand + i)
|
|
src_ds.GetRasterBand(i + 1).Fill(0)
|
|
src_ds.BuildOverviews("NEAREST", [2])
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=128", "SPARSE_OK=YES", "COMPRESS=JPEG"]
|
|
)
|
|
_check_cog(filename)
|
|
with gdaltest.config_option("GTIFF_HAS_OPTIMIZED_READ_MULTI_RANGE", "YES"):
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF") is None
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetMaskBand()
|
|
.GetMetadataItem("BLOCK_OFFSET_0_0", "TIFF")
|
|
is None
|
|
)
|
|
assert ds.GetRasterBand(1).ReadRaster(0, 0, 512, 512) == src_ds.GetRasterBand(
|
|
1
|
|
).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetMaskBand().ReadRaster(
|
|
0, 0, 512, 512
|
|
) == src_ds.GetRasterBand(4).ReadRaster(0, 0, 512, 512)
|
|
assert ds.GetRasterBand(1).GetOverview(0).ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
assert ds.GetRasterBand(1).GetOverview(0).GetMaskBand().ReadRaster(
|
|
0, 0, 256, 256
|
|
) == src_ds.GetRasterBand(4).GetOverview(0).ReadRaster(0, 0, 256, 256)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test ZOOM_LEVEL_STRATEGY option
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"zoom_level_strategy,expected_gt",
|
|
[
|
|
(
|
|
"AUTO",
|
|
(
|
|
-13110479.09147343,
|
|
76.43702828517416,
|
|
0.0,
|
|
4030983.1236470547,
|
|
0.0,
|
|
-76.43702828517416,
|
|
),
|
|
),
|
|
(
|
|
"LOWER",
|
|
(
|
|
-13110479.09147343,
|
|
76.43702828517416,
|
|
0.0,
|
|
4030983.1236470547,
|
|
0.0,
|
|
-76.43702828517416,
|
|
),
|
|
),
|
|
(
|
|
"UPPER",
|
|
(
|
|
-13100695.151852928,
|
|
38.21851414258708,
|
|
0.0,
|
|
4021199.1840265524,
|
|
0.0,
|
|
-38.21851414258708,
|
|
),
|
|
),
|
|
],
|
|
)
|
|
def test_cog_zoom_level_strategy(zoom_level_strategy, expected_gt):
|
|
|
|
filename = "/vsimem/test_cog_zoom_level_strategy.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"TILING_SCHEME=GoogleMapsCompatible",
|
|
"ZOOM_LEVEL_STRATEGY=" + zoom_level_strategy,
|
|
],
|
|
)
|
|
gt = ds.GetGeoTransform()
|
|
assert gt == pytest.approx(expected_gt, rel=1e-10)
|
|
|
|
# Test that the zoom level strategy applied on input data already on a
|
|
# zoom level doesn't lead to selecting another zoom level
|
|
filename2 = "/vsimem/test_cog_zoom_level_strategy_2.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
ds2 = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename2,
|
|
ds,
|
|
options=[
|
|
"TILING_SCHEME=GoogleMapsCompatible",
|
|
"ZOOM_LEVEL_STRATEGY=" + zoom_level_strategy,
|
|
],
|
|
)
|
|
gt = ds2.GetGeoTransform()
|
|
assert gt == pytest.approx(expected_gt, rel=1e-10)
|
|
ds2 = None
|
|
gdal.Unlink(filename2)
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test ZOOM_LEVEL option
|
|
|
|
|
|
def test_cog_zoom_level():
|
|
|
|
filename = "/vsimem/test_cog_zoom_level.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
|
|
with gdaltest.error_handler():
|
|
assert (
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "ZOOM_LEVEL=-1"],
|
|
)
|
|
is None
|
|
)
|
|
assert (
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "ZOOM_LEVEL=31"],
|
|
)
|
|
is None
|
|
)
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "ZOOM_LEVEL=12"],
|
|
)
|
|
gt = ds.GetGeoTransform()
|
|
expected_gt = (
|
|
-13100695.151852928,
|
|
38.21851414258813,
|
|
0.0,
|
|
4021199.1840265524,
|
|
0.0,
|
|
-38.21851414258813,
|
|
)
|
|
assert gt == pytest.approx(expected_gt, rel=1e-10)
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_cog_resampling_options():
|
|
|
|
filename = "/vsimem/test_cog_resampling_options.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "WARP_RESAMPLING=NEAREST"],
|
|
)
|
|
cs1 = ds.GetRasterBand(1).Checksum()
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "WARP_RESAMPLING=CUBIC"],
|
|
)
|
|
cs2 = ds.GetRasterBand(1).Checksum()
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"TILING_SCHEME=GoogleMapsCompatible",
|
|
"RESAMPLING=NEAREST",
|
|
"WARP_RESAMPLING=CUBIC",
|
|
],
|
|
)
|
|
cs3 = ds.GetRasterBand(1).Checksum()
|
|
|
|
assert cs1 != cs2
|
|
assert cs2 == cs3
|
|
|
|
src_ds = gdal.Translate("", "data/byte.tif", options="-of MEM -outsize 129 0")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=128", "OVERVIEW_RESAMPLING=NEAREST"]
|
|
)
|
|
cs1 = ds.GetRasterBand(1).GetOverview(0).Checksum()
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=128", "OVERVIEW_RESAMPLING=BILINEAR"]
|
|
)
|
|
cs2 = ds.GetRasterBand(1).GetOverview(0).Checksum()
|
|
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["BLOCKSIZE=128", "RESAMPLING=NEAREST", "OVERVIEW_RESAMPLING=BILINEAR"],
|
|
)
|
|
cs3 = ds.GetRasterBand(1).GetOverview(0).Checksum()
|
|
|
|
assert cs1 != cs2
|
|
assert cs2 == cs3
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_cog_invalid_warp_resampling():
|
|
|
|
filename = "/vsimem/test_cog_invalid_warp_resampling.tif"
|
|
src_ds = gdal.Open("data/byte.tif")
|
|
|
|
with gdaltest.error_handler():
|
|
assert (
|
|
gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["TILING_SCHEME=GoogleMapsCompatible", "RESAMPLING=INVALID"],
|
|
)
|
|
is None
|
|
)
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_cog_overview_size():
|
|
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 20480 // 4, 40960 // 4)
|
|
src_ds.SetGeoTransform([1723840, 7 * 4, 0, 5555840, 0, -7 * 4])
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(2193)
|
|
src_ds.SetProjection(srs.ExportToWkt())
|
|
filename = "/vsimem/test_cog_overview_size.tif"
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"TILING_SCHEME=NZTM2000",
|
|
"ALIGNED_LEVELS=4",
|
|
"OVERVIEW_RESAMPLING=NONE",
|
|
],
|
|
)
|
|
assert (ds.RasterXSize, ds.RasterYSize) == (20480 // 4, 40960 // 4)
|
|
ovr_size = [
|
|
(
|
|
ds.GetRasterBand(1).GetOverview(i).XSize,
|
|
ds.GetRasterBand(1).GetOverview(i).YSize,
|
|
)
|
|
for i in range(ds.GetRasterBand(1).GetOverviewCount())
|
|
]
|
|
assert ovr_size == [(2048, 4096), (1024, 2048), (512, 1024), (256, 512), (128, 256)]
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test bugfix for https://github.com/OSGeo/gdal/issues/2946
|
|
|
|
|
|
def test_cog_float32_color_table():
|
|
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 1024, 1024, 1, gdal.GDT_Float32)
|
|
src_ds.GetRasterBand(1).Fill(1.0)
|
|
ct = gdal.ColorTable()
|
|
src_ds.GetRasterBand(1).SetColorTable(ct)
|
|
filename = "/vsimem/test_cog_float32_color_table.tif"
|
|
# Silence warning about color table not being copied
|
|
with gdaltest.error_handler():
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(filename, src_ds) # segfault
|
|
assert ds
|
|
assert ds.GetRasterBand(1).GetColorTable() is None
|
|
assert struct.unpack("f", ds.ReadRaster(0, 0, 1, 1))[0] == 1.0
|
|
assert (
|
|
struct.unpack("f", ds.GetRasterBand(1).GetOverview(0).ReadRaster(0, 0, 1, 1))[0]
|
|
== 1.0
|
|
)
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test copy XMP
|
|
|
|
|
|
def test_cog_copy_xmp():
|
|
|
|
filename = "/vsimem/cog_xmp.tif"
|
|
src_ds = gdal.Open("../gdrivers/data/gtiff/byte_with_xmp.tif")
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(filename, src_ds)
|
|
assert ds
|
|
ds = None
|
|
|
|
ds = gdal.Open(filename)
|
|
xmp = ds.GetMetadata("xml:XMP")
|
|
ds = None
|
|
assert "W5M0MpCehiHzreSzNTczkc9d" in xmp[0], "Wrong input file without XMP"
|
|
_check_cog(filename)
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test creating COG from a source dataset that has overview with 'odd' sizes
|
|
# and a mask without overview
|
|
|
|
|
|
def test_cog_odd_overview_size_and_msk():
|
|
|
|
filename = "/vsimem/test_cog_odd_overview_size_and_msk.tif"
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 511, 511)
|
|
src_ds.BuildOverviews("NEAR", [2])
|
|
src_ds.CreateMaskBand(gdal.GMF_PER_DATASET)
|
|
ds = gdal.GetDriverByName("COG").CreateCopy(
|
|
filename, src_ds, options=["BLOCKSIZE=256"]
|
|
)
|
|
assert ds
|
|
assert ds.GetRasterBand(1).GetOverview(0).XSize == 256
|
|
assert ds.GetRasterBand(1).GetMaskBand().GetOverview(0).XSize == 256
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test turning on lossy WEBP compression if OVERVIEW_QUALITY < 100 specified
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "WEBP")
|
|
@pytest.mark.require_driver("WEBP")
|
|
def test_cog_webp_overview_turn_on_lossy_if_webp_level():
|
|
|
|
tmpfilename = "/vsimem/test_cog_webp_overview_turn_on_lossy_if_webp_level.tif"
|
|
|
|
gdal.Translate(
|
|
tmpfilename,
|
|
"../gdrivers/data/small_world.tif",
|
|
options="-of COG -outsize 513 0 -co COMPRESS=WEBP -co QUALITY=100 -co OVERVIEW_QUALITY=75",
|
|
)
|
|
|
|
ds = gdal.Open(tmpfilename)
|
|
assert (
|
|
ds.GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE") == "LOSSLESS"
|
|
)
|
|
assert (
|
|
ds.GetRasterBand(1)
|
|
.GetOverview(0)
|
|
.GetDataset()
|
|
.GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE")
|
|
== "LOSSY"
|
|
)
|
|
ds = None
|
|
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test lossless WEBP compression
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "WEBP")
|
|
@pytest.mark.require_driver("WEBP")
|
|
def test_cog_webp_lossless_webp():
|
|
|
|
tmpfilename = "/vsimem/test_cog_webp_lossless_webp.tif"
|
|
|
|
src_ds = gdal.Open("../gdrivers/data/small_world.tif")
|
|
gdal.ErrorReset()
|
|
gdal.Translate(
|
|
tmpfilename,
|
|
src_ds,
|
|
options="-of COG -co COMPRESS=WEBP -co QUALITY=100",
|
|
)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
ds = gdal.Open(tmpfilename)
|
|
assert (
|
|
ds.GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE") == "LOSSLESS"
|
|
)
|
|
assert ds.GetRasterBand(1).Checksum() == src_ds.GetRasterBand(1).Checksum()
|
|
ds = None
|
|
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test OVERVIEW_COUNT option
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"options,expected_count",
|
|
[
|
|
("", 1),
|
|
("-co OVERVIEW_COUNT=1", 1),
|
|
("-co OVERVIEW_COUNT=2", 2),
|
|
("-co OVERVIEW_COUNT=10", 8),
|
|
("-co TILING_SCHEME=GoogleMapsCompatible -co OVERVIEW_COUNT=1", 1),
|
|
("-co TILING_SCHEME=GoogleMapsCompatible -co OVERVIEW_COUNT=2", 2),
|
|
("-co TILING_SCHEME=GoogleMapsCompatible -co OVERVIEW_COUNT=10", 8),
|
|
],
|
|
)
|
|
def test_cog_overview_count(options, expected_count):
|
|
|
|
tmpfilename = "/vsimem/test_cog_overview_count.tif"
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.Translate(
|
|
tmpfilename,
|
|
"../gdrivers/data/small_world.tif",
|
|
options="-of COG -co BLOCKSIZE=256 " + options,
|
|
)
|
|
ds = gdal.Open(tmpfilename)
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == expected_count
|
|
ds = None
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test OVERVIEW_COUNT option with dataset with existing overviews
|
|
|
|
|
|
def test_cog_overview_count_existing():
|
|
|
|
tmpfilename = "/vsimem/test_cog_overview_count_existing.tif"
|
|
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 512, 512)
|
|
src_ds.GetRasterBand(1).Fill(255)
|
|
src_ds.BuildOverviews("NONE", [2, 4, 8, 16])
|
|
gdal.Translate(tmpfilename, src_ds, options="-of COG -co OVERVIEW_COUNT=3")
|
|
ds = gdal.Open(tmpfilename)
|
|
assert ds.GetRasterBand(1).GetOverviewCount() == 3
|
|
assert ds.GetRasterBand(1).GetOverview(0).Checksum() == 0
|
|
ds = None
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test JPEGXL compression with alpha
|
|
|
|
|
|
@pytest.mark.require_creation_option("COG", "JXL")
|
|
def test_cog_write_jpegxl_alpha():
|
|
|
|
src_ds = gdal.Open("data/stefan_full_rgba.tif")
|
|
filename = "/vsimem/test_tiff_write_jpegxl_alpha_distance_zero.tif"
|
|
|
|
gdal.GetDriverByName("GTiff").CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=[
|
|
"COMPRESS=JXL",
|
|
"JXL_LOSSLESS=NO",
|
|
"TILED=YES",
|
|
"BLOCKSIZE=512",
|
|
"BLOCKYSIZE=512",
|
|
],
|
|
)
|
|
ds = gdal.Open(filename)
|
|
ref_checksum = [ds.GetRasterBand(i + 1).Checksum() for i in range(4)]
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
drv = gdal.GetDriverByName("COG")
|
|
drv.CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["COMPRESS=JXL", "JXL_LOSSLESS=NO"],
|
|
)
|
|
ds = gdal.Open(filename)
|
|
assert [ds.GetRasterBand(i + 1).Checksum() for i in range(4)] == ref_checksum
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test JXL_ALPHA_DISTANCE creation option
|
|
|
|
|
|
@pytest.mark.require_creation_option(
|
|
"COG", "JXL_ALPHA_DISTANCE"
|
|
) # "libjxl > 0.8.1 required"
|
|
def test_cog_write_jpegxl_alpha_distance_zero():
|
|
|
|
drv = gdal.GetDriverByName("COG")
|
|
|
|
src_ds = gdal.Open("data/stefan_full_rgba.tif")
|
|
filename = "/vsimem/test_tiff_write_jpegxl_alpha_distance_zero.tif"
|
|
drv.CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["COMPRESS=JXL", "JXL_LOSSLESS=NO", "JXL_ALPHA_DISTANCE=0"],
|
|
)
|
|
ds = gdal.Open(filename)
|
|
assert float(ds.GetMetadataItem("JXL_ALPHA_DISTANCE", "IMAGE_STRUCTURE")) == 0
|
|
assert ds.GetRasterBand(1).Checksum() != src_ds.GetRasterBand(1).Checksum()
|
|
assert ds.GetRasterBand(4).Checksum() == src_ds.GetRasterBand(4).Checksum()
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test NBITS creation option
|
|
|
|
|
|
def test_cog_NBITS():
|
|
|
|
src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1)
|
|
drv = gdal.GetDriverByName("COG")
|
|
filename = "/vsimem/test_cog_NBITS.tif"
|
|
drv.CreateCopy(
|
|
filename,
|
|
src_ds,
|
|
options=["NBITS=7"],
|
|
)
|
|
ds = gdal.Open(filename)
|
|
assert ds.GetRasterBand(1).GetMetadataItem("NBITS", "IMAGE_STRUCTURE") == "7"
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|