Skip to content

Commit

Permalink
Add gdal raster resize
Browse files Browse the repository at this point in the history
Fixes #11908
  • Loading branch information
rouault committed Mar 7, 2025
1 parent fd803f4 commit 39e87d8
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 3 deletions.
1 change: 1 addition & 0 deletions apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ add_library(
gdalalg_raster_overview_delete.cpp
gdalalg_raster_read.cpp
gdalalg_raster_reproject.cpp
gdalalg_raster_resize.cpp
gdalalg_raster_stack.cpp
gdalalg_raster_write.cpp
gdalalg_vector.cpp
Expand Down
2 changes: 2 additions & 0 deletions apps/gdalalg_raster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "gdalalg_raster_overview.h"
#include "gdalalg_raster_pipeline.h"
#include "gdalalg_raster_reproject.h"
#include "gdalalg_raster_resize.h"
#include "gdalalg_raster_stack.h"

/************************************************************************/
Expand Down Expand Up @@ -50,6 +51,7 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
RegisterSubAlgorithm<GDALRasterPipelineAlgorithm>();
RegisterSubAlgorithm<GDALRasterReprojectAlgorithmStandalone>();
RegisterSubAlgorithm<GDALRasterMosaicAlgorithm>();
RegisterSubAlgorithm<GDALRasterResizeAlgorithmStandalone>();
RegisterSubAlgorithm<GDALRasterStackAlgorithm>();
}

Expand Down
2 changes: 2 additions & 0 deletions apps/gdalalg_raster_pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "gdalalg_raster_clip.h"
#include "gdalalg_raster_edit.h"
#include "gdalalg_raster_reproject.h"
#include "gdalalg_raster_resize.h"
#include "gdalalg_raster_write.h"

#include "cpl_conv.h"
Expand Down Expand Up @@ -165,6 +166,7 @@ GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
m_stepRegistry.Register<GDALRasterClipAlgorithm>();
m_stepRegistry.Register<GDALRasterEditAlgorithm>();
m_stepRegistry.Register<GDALRasterReprojectAlgorithm>();
m_stepRegistry.Register<GDALRasterResizeAlgorithm>();
}

/************************************************************************/
Expand Down
1 change: 1 addition & 0 deletions apps/gdalalg_raster_reproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep)
.SetChoices("nearest", "bilinear", "cubic", "cubicspline", "lanczos",
"average", "rms", "mode", "min", "max", "med", "q1", "q3",
"sum")
.SetDefault("nearest")
.SetHiddenChoices("near");

auto &resArg =
Expand Down
103 changes: 103 additions & 0 deletions apps/gdalalg_raster_resize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/******************************************************************************
*
* Project: GDAL
* Purpose: "resize" step of "raster pipeline"
* Author: Even Rouault <even dot rouault at spatialys.com>
*
******************************************************************************
* Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
*
* SPDX-License-Identifier: MIT
****************************************************************************/

#include "gdalalg_raster_resize.h"

#include "gdal_priv.h"
#include "gdal_utils.h"

//! @cond Doxygen_Suppress

#ifndef _
#define _(x) (x)
#endif

/************************************************************************/
/* GDALRasterResizeAlgorithm::GDALRasterResizeAlgorithm() */
/************************************************************************/

GDALRasterResizeAlgorithm::GDALRasterResizeAlgorithm(bool standaloneStep)
: GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
standaloneStep)
{
auto &sizeArg = AddArg("size", 0, _("Target size in pixels"), &m_size)
.SetMinCount(2)
.SetMaxCount(2)
.SetRequired()
.SetRepeatedArgAllowed(false)
.SetDisplayHintAboutRepetition(false)
.SetMetaVar("<width>,<height>")
.SetMutualExclusionGroup("resolution-size");
sizeArg.AddValidationAction(
[&sizeArg]()
{
const auto &val = sizeArg.Get<std::vector<int>>();
CPLAssert(val.size() == 2);
if (!(val[0] >= 0 && val[1] >= 0))
{
CPLError(CE_Failure, CPLE_AppDefined,
"Target size should be positive or 0.");
return false;
}
return true;
});

AddArg("resampling", 'r', _("Resampling method"), &m_resampling)
.SetChoices("nearest", "bilinear", "cubic", "cubicspline", "lanczos",
"average", "mode")
.SetDefault("nearest")
.SetHiddenChoices("near");
}

/************************************************************************/
/* GDALRasterResizeAlgorithm::RunStep() */
/************************************************************************/

bool GDALRasterResizeAlgorithm::RunStep(GDALProgressFunc, void *)
{
CPLAssert(m_inputDataset.GetDatasetRef());
CPLAssert(m_outputDataset.GetName().empty());
CPLAssert(!m_outputDataset.GetDatasetRef());

CPLStringList aosOptions;
aosOptions.AddString("-of");
aosOptions.AddString("VRT");
if (!m_size.empty())
{
aosOptions.AddString("-outsize");
aosOptions.AddString(CPLSPrintf("%d", m_size[0]));
aosOptions.AddString(CPLSPrintf("%d", m_size[1]));
}
if (!m_resampling.empty())
{
aosOptions.AddString("-r");
aosOptions.AddString(m_resampling.c_str());
}

GDALTranslateOptions *psOptions =
GDALTranslateOptionsNew(aosOptions.List(), nullptr);

auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
GDALTranslate(m_outputDataset.GetName().c_str(),
GDALDataset::ToHandle(m_inputDataset.GetDatasetRef()),
psOptions, nullptr)));
GDALTranslateOptionsFree(psOptions);
const bool bRet = poOutDS != nullptr;
if (poOutDS)
{
m_outputDataset.Set(std::move(poOutDS));
}

return bRet;
}

//! @endcond
63 changes: 63 additions & 0 deletions apps/gdalalg_raster_resize.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/******************************************************************************
*
* Project: GDAL
* Purpose: "resize" step of "raster pipeline"
* Author: Even Rouault <even dot rouault at spatialys.com>
*
******************************************************************************
* Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
*
* SPDX-License-Identifier: MIT
****************************************************************************/

#ifndef GDALALG_RASTER_RESIZE_INCLUDED
#define GDALALG_RASTER_RESIZE_INCLUDED

#include "gdalalg_raster_pipeline.h"

//! @cond Doxygen_Suppress

/************************************************************************/
/* GDALRasterResizeAlgorithm */
/************************************************************************/

class GDALRasterResizeAlgorithm /* non final */
: public GDALRasterPipelineStepAlgorithm
{
public:
static constexpr const char *NAME = "resize";
static constexpr const char *DESCRIPTION =
"Resize a raster dataset without changing the georeferenced extents.";
static constexpr const char *HELP_URL = "/programs/gdal_raster_resize.html";

static std::vector<std::string> GetAliases()
{
return {};
}

explicit GDALRasterResizeAlgorithm(bool standaloneStep = false);

private:
bool RunStep(GDALProgressFunc pfnProgress, void *pProgressData) override;

std::vector<int> m_size{};
std::string m_resampling{};
};

/************************************************************************/
/* GDALRasterResizeAlgorithmStandalone */
/************************************************************************/

class GDALRasterResizeAlgorithmStandalone final
: public GDALRasterResizeAlgorithm
{
public:
GDALRasterResizeAlgorithmStandalone()
: GDALRasterResizeAlgorithm(/* standaloneStep = */ true)
{
}
};

//! @endcond

#endif /* GDALALG_RASTER_RESIZE_INCLUDED */
76 changes: 76 additions & 0 deletions autotest/utilities/test_gdalalg_raster_resize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env pytest
# -*- coding: utf-8 -*-
###############################################################################
# Project: GDAL/OGR Test Suite
# Purpose: 'gdal raster resize' testing
# Author: Even Rouault <even dot rouault @ spatialys.com>
#
###############################################################################
# Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
#
# SPDX-License-Identifier: MIT
###############################################################################

from osgeo import gdal


def get_resize_alg():
reg = gdal.GetGlobalAlgorithmRegistry()
raster = reg.InstantiateAlg("raster")
return raster.InstantiateSubAlgorithm("resize")


def test_gdalalg_raster_resize(tmp_vsimem):

out_filename = str(tmp_vsimem / "out.tif")

last_pct = [0]

def my_progress(pct, msg, user_data):
last_pct[0] = pct
return True

pipeline = get_resize_alg()
assert pipeline.ParseRunAndFinalize(
[
"--size=10,0",
"../gcore/data/byte.tif",
out_filename,
],
my_progress,
)
assert last_pct[0] == 1.0

with gdal.OpenEx(out_filename) as ds:
assert ds.RasterXSize == 10
assert ds.RasterYSize == 10
assert ds.GetRasterBand(1).Checksum() == 1192


def test_gdalalg_raster_resize_resampling(tmp_vsimem):

out_filename = str(tmp_vsimem / "out.tif")

last_pct = [0]

def my_progress(pct, msg, user_data):
last_pct[0] = pct
return True

pipeline = get_resize_alg()
assert pipeline.ParseRunAndFinalize(
[
"--size=0,10",
"-r",
"cubic",
"../gcore/data/byte.tif",
out_filename,
],
my_progress,
)
assert last_pct[0] == 1.0

with gdal.OpenEx(out_filename) as ds:
assert ds.RasterXSize == 10
assert ds.RasterYSize == 10
assert ds.GetRasterBand(1).Checksum() == 1059
7 changes: 7 additions & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@
[author_evenr],
1,
),
(
"programs/gdal_raster_resize",
"gdal-raster-resize",
"Resize a raster dataset",
[author_evenr],
1,
),
(
"programs/gdal_raster_stack",
"gdal-raster-stack",
Expand Down
2 changes: 2 additions & 0 deletions doc/source/programs/gdal_raster.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Synopsis
- overview: Manage overviews of a raster dataset.
- pipeline: Process a raster dataset.
- reproject: Reproject a raster dataset.
- resize: Resize a raster dataset.
- stack: Combine together input bands into a multi-band output, either virtual (VRT) or materialized.
Expand All @@ -42,6 +43,7 @@ Available sub-commands
- :ref:`gdal_raster_overview_subcommand`
- :ref:`gdal_raster_pipeline_subcommand`
- :ref:`gdal_raster_reproject_subcommand`
- :ref:`gdal_raster_resize_subcommand`
- :ref:`gdal_raster_stack_subcommand`

Examples
Expand Down
14 changes: 13 additions & 1 deletion doc/source/programs/gdal_raster_pipeline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,25 @@ Details for options can be found in :ref:`gdal_raster_edit_subcommand`.
Options:
-s, --src-crs <SRC-CRS> Source CRS
-d, --dst-crs <DST-CRS> Destination CRS
-r, --resampling <RESAMPLING> Resampling method. RESAMPLING=near|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum
-r, --resampling <RESAMPLING> Resampling method. RESAMPLING=near|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum (default: nearest)
--resolution <xres>,<yres> Target resolution (in destination CRS units)
--bbox <xmin>,<ymin>,<xmax>,<ymax> Target bounding box (in destination CRS units)
--target-aligned-pixels Round target extent to target resolution
Details for options can be found in :ref:`gdal_raster_reproject_subcommand`.

* resize [OPTIONS]

.. code-block::
Resize a raster dataset without changing the georeferenced extents.
Options:
--size <width>,<height> Target size in pixels [required]
-r, --resampling <RESAMPLING> Resampling method. RESAMPLING=nearest|bilinear|cubic|cubicspline|lanczos|average|mode (default: nearest)
Details for options can be found in :ref:`gdal_raster_resize_subcommand`.

* write [OPTIONS] <OUTPUT>

.. code-block::
Expand Down
2 changes: 1 addition & 1 deletion doc/source/programs/gdal_raster_reproject.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Synopsis
--overwrite Whether overwriting existing output is allowed
-s, --src-crs <SRC-CRS> Source CRS
-d, --dst-crs <DST-CRS> Destination CRS
-r, --resampling <RESAMPLING> Resampling method. RESAMPLING=nearest|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum
-r, --resampling <RESAMPLING> Resampling method. RESAMPLING=nearest|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum (default: nearest)
--resolution <xres>,<yres> Target resolution (in destination CRS units)
--bbox <xmin>,<ymin>,<xmax>,<ymax> Target bounding box (in destination CRS units)
--target-aligned-pixels Round target extent to target resolution
Expand Down
Loading

0 comments on commit 39e87d8

Please sign in to comment.