Skip to content

Commit

Permalink
gdal2tiles: add --nodata-values-pct-threshold option
Browse files Browse the repository at this point in the history
This is similar to the --excluded-values-pct-threshold option added in
OSGeo#9631, but here this controls the
minimum amount of transparent/invalid/nodata source pixels to cause the
target pixels to be transparent.
Only used currently for average resampling
  • Loading branch information
rouault committed May 3, 2024
1 parent 7aca95d commit a5323ac
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 3 deletions.
55 changes: 55 additions & 0 deletions autotest/pyscripts/test_gdal2tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,61 @@ def test_gdal2tiles_excluded_values(script_path, tmp_path):
)


@pytest.mark.require_driver("PNG")
def test_gdal2tiles_nodata_values_pct_threshold(script_path, tmp_path):

input_tif = str(tmp_path / "test_gdal2tiles_nodata_values_pct_threshold.tif")
output_folder = str(tmp_path / "test_gdal2tiles_nodata_values_pct_threshold")

src_ds = gdal.GetDriverByName("GTiff").Create(input_tif, 256, 256, 1, gdal.GDT_Byte)
src_ds.GetRasterBand(1).SetNoDataValue(20)
src_ds.GetRasterBand(1).WriteRaster(
0, 0, 2, 2, struct.pack("B" * 4, 10, 20, 30, 40)
)
srs = osr.SpatialReference()
srs.ImportFromEPSG(3857)
src_ds.SetSpatialRef(srs)
MAX_GM = 20037508.342789244
RES_Z0 = 2 * MAX_GM / 256
RES_Z1 = RES_Z0 / 2
# Spatial extent of tile (0,0) at zoom level 1
src_ds.SetGeoTransform([-MAX_GM, RES_Z1, 0, MAX_GM, 0, -RES_Z1])
src_ds = None

test_py_scripts.run_py_script_as_external_script(
script_path,
"gdal2tiles",
f"-q -z 0-1 {input_tif} {output_folder}",
)

ds = gdal.Open(f"{output_folder}/0/0/0.png")
assert struct.unpack("B" * 2, ds.ReadRaster(0, 0, 1, 1)) == (
round((10 + 30 + 40) / 3),
255,
)

test_py_scripts.run_py_script_as_external_script(
script_path,
"gdal2tiles",
f"-q -z 0-1 --nodata-values-pct-threshold=50 {input_tif} {output_folder}",
)

ds = gdal.Open(f"{output_folder}/0/0/0.png")
assert struct.unpack("B" * 2, ds.ReadRaster(0, 0, 1, 1)) == (
round((10 + 30 + 40) / 3),
255,
)

test_py_scripts.run_py_script_as_external_script(
script_path,
"gdal2tiles",
f"-q -z 0-1 --nodata-values-pct-threshold=25 {input_tif} {output_folder}",
)

ds = gdal.Open(f"{output_folder}/0/0/0.png")
assert struct.unpack("B" * 2, ds.ReadRaster(0, 0, 1, 1)) == (0, 0)


@pytest.mark.require_driver("JPEG")
@pytest.mark.parametrize(
"resampling, expected_stats_z0, expected_stats_z1",
Expand Down
16 changes: 15 additions & 1 deletion doc/source/programs/gdal2tiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Synopsis
[-w <webviewer>] [-t <title>] [-c <copyright>]
[--processes=<NB_PROCESSES>] [--mpi] [--xyz]
[--tilesize=<PIXELS>] --tiledriver=<DRIVER> [--tmscompatible]
[--excluded-values=<EXCLUDED_VALUES>] [--excluded-values-pct-threshold=<EXCLUDED_VALUES_PCT_THRESHOLD>]
[--excluded-values=<EXCLUDED_VALUES>]
[--excluded-values-pct-threshold=<EXCLUDED_VALUES_PCT_THRESHOLD>]
[--nodata-values-pct-threshold=<NODATA_VALUES_PCT_THRESHOLD>]
[-g <googlekey] [-b <bingkey>] <input_file> [<output_dir>] [<COMMON_OPTIONS>]
Description
Expand Down Expand Up @@ -155,13 +157,25 @@ can publish a picture without proper georeferencing too.
that pixels matching one of the excluded value tuples are still considered
as valid, when determining the target pixel validity/density.

.. versionadded:: 3.9

.. option:: --excluded-values-pct-threshold=EXCLUDED_VALUES_PCT_THRESHOLD

Minimum percentage of source pixels that must be set at one of the --excluded-values to cause the excluded
value, that is in majority among source pixels, to be used as the target pixel value. Default value is 50(%)

.. versionadded:: 3.9

.. option:: --nodata-values-pct-threshold=<NODATA_VALUES_PCT_THRESHOLD>

Minimum percentage of source pixels that must be at nodata (or alpha=0 or any
other way to express transparent pixel) to cause the target pixel value to
be transparent. Default value is 100 (%), which means that a target pixel is
transparent only if all contributing source pixels are transparent.
Only taken into account for average resampling.

.. versionadded:: 3.9

.. option:: -h, --help

Show help message and exit.
Expand Down
16 changes: 14 additions & 2 deletions swig/python/gdal-utils/osgeo_utils/gdal2tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,12 +885,17 @@ def scale_query_to_tile(dsquery, dstile, options, tilefilename=""):
)
dstile.SetGeoTransform((0.0, 1.0, 0.0, 0.0, 0.0, 1.0))

if options.resampling == "average" and options.excluded_values:
if options.resampling == "average" and (
options.excluded_values or options.nodata_values_pct_threshold < 100
):

gdal.Warp(
dstile,
dsquery,
options=f"-r average -wo EXCLUDED_VALUES={options.excluded_values} -wo EXCLUDED_VALUES_PCT_THRESHOLD={options.excluded_values_pct_threshold}",
options="-r average "
+ f"-wo EXCLUDED_VALUES={options.excluded_values} "
+ f"-wo EXCLUDED_VALUES_PCT_THRESHOLD={options.excluded_values_pct_threshold} "
+ f"-wo NODATA_VALUES_PCT_THRESHOLD={options.nodata_values_pct_threshold}",
)

elif options.resampling == "average":
Expand Down Expand Up @@ -1872,6 +1877,13 @@ def optparse_init() -> optparse.OptionParser:
default=50,
help="Minimum percentage of source pixels that must be set at one of the --excluded-values to cause the excluded value, that is in majority among source pixels, to be used as the target pixel value. Default value is 50 (%)",
)
p.add_option(
"--nodata-values-pct-threshold",
dest="nodata_values_pct_threshold",
type=float,
default=100,
help="Minimum percentage of source pixels that must be at nodata (or alpha=0 or any other way to express transparent pixel) to cause the target pixel value to be transparent. Default value is 100 (%). Only taken into account for average resampling",
)

# KML options
g = optparse.OptionGroup(
Expand Down

0 comments on commit a5323ac

Please sign in to comment.