Skip to content

Commit b0c7330

Browse files
authored
Merge pull request OSGeo#12208 from elpaso/cli-gdal-fillnodata
[cli] Port gdal_fillnodata to CLI
1 parent cbdd049 commit b0c7330

File tree

11 files changed

+490
-2
lines changed

11 files changed

+490
-2
lines changed

apps/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_library(
2727
gdalalg_raster_edit.cpp
2828
gdalalg_raster_contour.cpp
2929
gdalalg_raster_footprint.cpp
30+
gdalalg_raster_fillnodata.cpp
3031
gdalalg_raster_hillshade.cpp
3132
gdalalg_raster_index.cpp
3233
gdalalg_raster_mosaic.cpp

apps/gdalalg_raster.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "gdalalg_raster_edit.h"
2525
#include "gdalalg_raster_contour.h"
2626
#include "gdalalg_raster_footprint.h"
27+
#include "gdalalg_raster_fillnodata.h"
2728
#include "gdalalg_raster_hillshade.h"
2829
#include "gdalalg_raster_index.h"
2930
#include "gdalalg_raster_mosaic.h"
@@ -67,6 +68,7 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
6768
RegisterSubAlgorithm<GDALRasterEditAlgorithmStandalone>();
6869
RegisterSubAlgorithm<GDALRasterFootprintAlgorithm>();
6970
RegisterSubAlgorithm<GDALRasterHillshadeAlgorithmStandalone>();
71+
RegisterSubAlgorithm<GDALRasterFillNodataAlgorithm>();
7072
RegisterSubAlgorithm<GDALRasterIndexAlgorithm>();
7173
RegisterSubAlgorithm<GDALRasterOverviewAlgorithm>();
7274
RegisterSubAlgorithm<GDALRasterPipelineAlgorithm>();

apps/gdalalg_raster_fillnodata.cpp

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: "gdal raster fillnodata" standalone command
5+
* Author: Alessandro Pasotti <elpaso at itopen dot it>
6+
*
7+
******************************************************************************
8+
* Copyright (c) 2025, Alessandro Pasotti <elpaso at itopen dot it>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#include "gdalalg_raster_fillnodata.h"
14+
15+
#include "gdal_priv.h"
16+
#include "gdal_utils.h"
17+
#include "gdal_alg.h"
18+
19+
#include <algorithm>
20+
21+
//! @cond Doxygen_Suppress
22+
23+
#ifndef _
24+
#define _(x) (x)
25+
#endif
26+
27+
/************************************************************************/
28+
/* GDALRasterFillNodataAlgorithm::GDALRasterFillNodataAlgorithm() */
29+
/************************************************************************/
30+
31+
GDALRasterFillNodataAlgorithm::GDALRasterFillNodataAlgorithm() noexcept
32+
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
33+
{
34+
35+
AddProgressArg();
36+
37+
AddInputFormatsArg(&m_inputFormats)
38+
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
39+
AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
40+
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
41+
AddOutputFormatArg(&m_format, /* bStreamAllowed = */ false,
42+
/* bGDALGAllowed = */ false)
43+
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
44+
{GDAL_DCAP_CREATE, GDAL_DCAP_RASTER});
45+
46+
AddCreationOptionsArg(&m_creationOptions);
47+
AddOverwriteArg(&m_overwrite);
48+
49+
AddBandArg(&m_band).SetDefault(m_band);
50+
51+
AddArg("max-distance", 'd',
52+
_("The maximum distance (in pixels) that the algorithm will search "
53+
"out for values to interpolate."),
54+
&m_maxDistance)
55+
.SetDefault(m_maxDistance)
56+
.SetMetaVar("MAX_DISTANCE");
57+
58+
AddArg("smoothing-iterations", 's',
59+
_("The number of 3x3 average filter smoothing iterations to run "
60+
"after the interpolation to dampen artifacts. The default is zero "
61+
"smoothing iterations."),
62+
&m_smoothingIterations)
63+
.SetDefault(m_smoothingIterations)
64+
.SetMetaVar("SMOOTHING_ITERATIONS");
65+
66+
auto &mask{AddArg("mask", 0,
67+
_("Use the first band of the specified file as a "
68+
"validity mask (zero is invalid, non-zero is valid)."),
69+
&m_maskDataset)};
70+
71+
SetAutoCompleteFunctionForFilename(mask, GDAL_OF_RASTER);
72+
73+
mask.AddValidationAction(
74+
[&mask]()
75+
{
76+
// Check if the mask dataset is a valid raster dataset descriptor
77+
// Try to open the dataset as a raster
78+
std::unique_ptr<GDALDataset> poDS(
79+
GDALDataset::Open(mask.Get<std::string>().c_str(),
80+
GDAL_OF_RASTER, nullptr, nullptr, nullptr));
81+
return static_cast<bool>(poDS);
82+
});
83+
84+
AddArg("strategy", 0,
85+
_("By default, pixels are interpolated using an inverse distance "
86+
"weighting (invdist). It is also possible to choose a nearest "
87+
"neighbour (nearest) strategy."),
88+
&m_strategy)
89+
.SetDefault(m_strategy)
90+
.SetChoices("invdist", "nearest");
91+
}
92+
93+
/************************************************************************/
94+
/* GDALRasterFillNodataAlgorithm::RunImpl() */
95+
/************************************************************************/
96+
97+
bool GDALRasterFillNodataAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
98+
void *pProgressData)
99+
{
100+
101+
VSIStatBufL sStat;
102+
if (!m_overwrite && !m_outputDataset.GetName().empty() &&
103+
(VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
104+
std::unique_ptr<GDALDataset>(
105+
GDALDataset::Open(m_outputDataset.GetName().c_str()))))
106+
{
107+
ReportError(CE_Failure, CPLE_AppDefined,
108+
"File '%s' already exists. Specify the --overwrite "
109+
"option to overwrite it.",
110+
m_outputDataset.GetName().c_str());
111+
return false;
112+
}
113+
114+
CPLStringList aosOptions;
115+
aosOptions.AddString("-of");
116+
aosOptions.AddString(m_format.c_str());
117+
aosOptions.AddString("-b");
118+
aosOptions.AddString(CPLSPrintf("%d", m_band));
119+
120+
for (const auto &co : m_creationOptions)
121+
{
122+
aosOptions.AddString("-co");
123+
aosOptions.AddString(co.c_str());
124+
}
125+
126+
GDALTranslateOptions *psOptions =
127+
GDALTranslateOptionsNew(aosOptions.List(), nullptr);
128+
129+
GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
130+
auto poRetDS =
131+
std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALTranslate(
132+
m_outputDataset.GetName().c_str(), hSrcDS, psOptions, nullptr)));
133+
GDALTranslateOptionsFree(psOptions);
134+
135+
if (!poRetDS)
136+
{
137+
return false;
138+
}
139+
140+
GDALRasterBand *maskBand{nullptr};
141+
if (m_maskDataset.GetDatasetRef())
142+
{
143+
maskBand = m_maskDataset.GetDatasetRef()->GetRasterBand(1);
144+
if (!maskBand)
145+
{
146+
ReportError(CE_Failure, CPLE_AppDefined, "Cannot get mask band.");
147+
return false;
148+
}
149+
}
150+
151+
GDALRasterBand *dstBand{poRetDS->GetRasterBand(1)};
152+
// Prepare options to pass to GDALFillNodata
153+
CPLStringList aosFillOptions;
154+
155+
if (EQUAL(m_strategy.c_str(), "nearest"))
156+
aosFillOptions.AddNameValue("INTERPOLATION", "NEAREST");
157+
else
158+
aosFillOptions.AddNameValue("INTERPOLATION",
159+
"INV_DIST"); // default strategy
160+
161+
auto retVal{GDALFillNodata(
162+
dstBand, maskBand, m_maxDistance, 0, m_smoothingIterations,
163+
aosFillOptions.List(), m_progressBarRequested ? pfnProgress : nullptr,
164+
m_progressBarRequested ? pProgressData : nullptr)};
165+
if (retVal != CE_None)
166+
{
167+
ReportError(CE_Failure, CPLE_AppDefined, "Cannot run fillNodata.");
168+
return false;
169+
}
170+
171+
poRetDS->FlushCache();
172+
173+
m_outputDataset.Set(std::move(poRetDS));
174+
175+
return true;
176+
}
177+
178+
//! @endcond

apps/gdalalg_raster_fillnodata.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: "gdal raster fillnodata" standalone command
5+
* Author: Alessandro Pasotti <elpaso at itopen dot it>
6+
*
7+
******************************************************************************
8+
* Copyright (c) 2025, Alessandro Pasotti <elpaso at itopen dot it>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#ifndef GDALALG_RASTER_FILLNODATA_INCLUDED
14+
#define GDALALG_RASTER_FILLNODATA_INCLUDED
15+
16+
#include "gdalalg_raster_pipeline.h"
17+
18+
//! @cond Doxygen_Suppress
19+
20+
/************************************************************************/
21+
/* GDALRasterFillNodataAlgorithm */
22+
/************************************************************************/
23+
24+
class GDALRasterFillNodataAlgorithm /* non final */
25+
: public GDALAlgorithm
26+
{
27+
public:
28+
static constexpr const char *NAME = "fillnodata";
29+
static constexpr const char *DESCRIPTION =
30+
"Fill nodata raster regions by interpolation from edges.";
31+
static constexpr const char *HELP_URL =
32+
"/programs/gdal_raster_fillnodata.html";
33+
34+
explicit GDALRasterFillNodataAlgorithm() noexcept;
35+
36+
private:
37+
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
38+
39+
GDALArgDatasetValue m_inputDataset{};
40+
GDALArgDatasetValue m_outputDataset{};
41+
std::string m_format{};
42+
std::vector<std::string> m_creationOptions{};
43+
std::vector<std::string> m_inputFormats{};
44+
45+
bool m_overwrite{false};
46+
// The maximum distance (in pixels) that the algorithm will search out for values to interpolate. The default is 100 pixels.
47+
int m_maxDistance = 100;
48+
// The number of 3x3 average filter smoothing iterations to run after the interpolation to dampen artifacts. The default is zero smoothing iterations.
49+
int m_smoothingIterations = 0;
50+
// The band to operate on, by default the first band is operated on.
51+
int m_band = 1;
52+
// Use the first band of the specified file as a validity mask (zero is invalid, non-zero is valid).
53+
GDALArgDatasetValue m_maskDataset{};
54+
// By default, pixels are interpolated using an inverse distance weighting (inv_dist). It is also possible to choose a nearest neighbour (nearest) strategy.
55+
std::string m_strategy = "invdist";
56+
};
57+
58+
//! @endcond
59+
60+
#endif /* GDALALG_RASTER_FILLNODATA_INCLUDED */

apps/gdalalg_raster_pipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "gdalalg_raster_clip.h"
1818
#include "gdalalg_raster_color_map.h"
1919
#include "gdalalg_raster_edit.h"
20+
#include "gdalalg_raster_fillnodata.h"
2021
#include "gdalalg_raster_hillshade.h"
2122
#include "gdalalg_raster_reproject.h"
2223
#include "gdalalg_raster_resize.h"

apps/gdalalg_raster_pipeline.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ class GDALRasterPipelineStepAlgorithm /* non final */ : public GDALAlgorithm
5555
std::vector<std::string> m_creationOptions{};
5656
bool m_overwrite = false;
5757
std::string m_outputLayerName{};
58-
59-
private:
6058
GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr;
6159

60+
private:
6261
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
6362
GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override;
6463
bool CheckSafeForStreamOutput() override;

0 commit comments

Comments
 (0)