Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_mosaic_stack_common.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Common code of "raster mosaic" and "raster stack"
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_raster_mosaic_stack_common.h"
14
#include "gdalalg_raster_write.h"
15
16
#include "cpl_conv.h"
17
#include "cpl_vsi_virtual.h"
18
19
#include "gdal_priv.h"
20
#include "gdal_utils.h"
21
22
//! @cond Doxygen_Suppress
23
24
#ifndef _
25
0
#define _(x) (x)
26
#endif
27
28
/************************************************************************/
29
/*                       GetConstructorOptions()                        */
30
/************************************************************************/
31
32
/* static */ GDALRasterMosaicStackCommonAlgorithm::ConstructorOptions
33
GDALRasterMosaicStackCommonAlgorithm::GetConstructorOptions(bool standaloneStep)
34
0
{
35
0
    ConstructorOptions opts;
36
0
    opts.SetStandaloneStep(standaloneStep);
37
0
    opts.SetAutoOpenInputDatasets(false);
38
0
    opts.SetInputDatasetHelpMsg(
39
0
        _("Input raster datasets (or specify a @<filename> to point to a "
40
0
          "file containing filenames)"));
41
0
    opts.SetAddDefaultArguments(false);
42
0
    opts.SetInputDatasetMaxCount(INT_MAX);
43
0
    return opts;
44
0
}
45
46
/************************************************************************/
47
/*                GDALRasterMosaicStackCommonAlgorithm()                */
48
/************************************************************************/
49
50
GDALRasterMosaicStackCommonAlgorithm::GDALRasterMosaicStackCommonAlgorithm(
51
    const std::string &name, const std::string &description,
52
    const std::string &helpURL, bool bStandalone)
53
0
    : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
54
0
                                      GetConstructorOptions(bStandalone))
55
0
{
56
0
    AddRasterInputArgs(/* openForMixedRasterVector = */ false,
57
0
                       /* hiddenForCLI = */ false);
58
0
    if (bStandalone)
59
0
    {
60
0
        AddProgressArg();
61
0
        AddRasterOutputArgs(false);
62
0
    }
63
64
0
    AddBandArg(&m_bands);
65
0
    AddAbsolutePathArg(
66
0
        &m_writeAbsolutePaths,
67
0
        _("Whether the path to the input datasets should be stored as an "
68
0
          "absolute path"));
69
70
0
    auto &resArg =
71
0
        AddArg("resolution", 0,
72
0
               _("Target resolution (in destination CRS units)"), &m_resolution)
73
0
            .SetDefault("same")
74
0
            .SetMetaVar("<xres>,<yres>|same|average|common|highest|lowest");
75
0
    resArg.AddValidationAction(
76
0
        [this, &resArg]()
77
0
        {
78
0
            const std::string val = resArg.Get<std::string>();
79
0
            if (val != "average" && val != "highest" && val != "lowest" &&
80
0
                val != "same" && val != "common")
81
0
            {
82
0
                const auto aosTokens =
83
0
                    CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
84
0
                if (aosTokens.size() != 2 ||
85
0
                    CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
86
0
                    CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
87
0
                    CPLAtof(aosTokens[0]) <= 0 || CPLAtof(aosTokens[1]) <= 0)
88
0
                {
89
0
                    ReportError(CE_Failure, CPLE_AppDefined,
90
0
                                "resolution: two comma separated positive "
91
0
                                "values should be provided, or 'same', "
92
0
                                "'average', 'common', 'highest' or 'lowest'");
93
0
                    return false;
94
0
                }
95
0
            }
96
0
            return true;
97
0
        });
98
99
0
    AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
100
0
                          "destination CRS units)"));
101
0
    auto &tapArg = AddArg("target-aligned-pixels", 0,
102
0
                          _("Round target extent to target resolution"),
103
0
                          &m_targetAlignedPixels)
104
0
                       .AddHiddenAlias("tap");
105
0
    AddArg("src-nodata", 0, _("Set nodata values for input bands."),
106
0
           &m_srcNoData)
107
0
        .SetMinCount(1)
108
0
        .SetRepeatedArgAllowed(false);
109
0
    AddArg("dst-nodata", 0,
110
0
           _("Set nodata values at the destination band level."), &m_dstNoData)
111
0
        .SetMinCount(1)
112
0
        .SetRepeatedArgAllowed(false);
113
0
    AddArg("hide-nodata", 0,
114
0
           _("Makes the destination band not report the NoData."),
115
0
           &m_hideNoData);
116
117
0
    AddValidationAction(
118
0
        [this, &resArg, &tapArg]()
119
0
        {
120
0
            if (tapArg.IsExplicitlySet() && !resArg.IsExplicitlySet())
121
0
            {
122
0
                ReportError(
123
0
                    CE_Failure, CPLE_IllegalArg,
124
0
                    "Argument 'target-aligned-pixels' can only be specified if "
125
0
                    "argument 'resolution' is also specified.");
126
0
                return false;
127
0
            }
128
0
            return true;
129
0
        });
130
0
}
131
132
/************************************************************************/
133
/*     GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames()     */
134
/************************************************************************/
135
136
bool GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames(
137
    GDALPipelineStepRunContext &ctxt,
138
    std::vector<GDALDatasetH> &ahInputDatasets,
139
    CPLStringList &aosInputDatasetNames, bool &foundByName)
140
0
{
141
0
    bool foundByRef = false;
142
0
    for (auto &ds : m_inputDataset)
143
0
    {
144
0
        if (ds.GetDatasetRef())
145
0
        {
146
0
            foundByRef = true;
147
0
            ahInputDatasets.push_back(
148
0
                GDALDataset::ToHandle(ds.GetDatasetRef()));
149
0
        }
150
0
        else if (!ds.GetName().empty())
151
0
        {
152
0
            foundByName = true;
153
0
            if (ds.GetName()[0] == '@')
154
0
            {
155
0
                auto f = VSIVirtualHandleUniquePtr(
156
0
                    VSIFOpenL(ds.GetName().c_str() + 1, "r"));
157
0
                if (!f)
158
0
                {
159
0
                    ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
160
0
                                ds.GetName().c_str() + 1);
161
0
                    return false;
162
0
                }
163
0
                while (const char *filename = CPLReadLineL(f.get()))
164
0
                {
165
0
                    aosInputDatasetNames.push_back(filename);
166
0
                }
167
0
            }
168
0
            else if (ds.GetName().find_first_of("*?[") != std::string::npos)
169
0
            {
170
0
                CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
171
0
                                                 ctxt.m_pfnProgress,
172
0
                                                 ctxt.m_pProgressData));
173
0
                for (const char *pszStr : aosMatches)
174
0
                {
175
0
                    aosInputDatasetNames.push_back(pszStr);
176
0
                }
177
0
            }
178
0
            else
179
0
            {
180
0
                std::string osDatasetName = ds.GetName();
181
0
                if (!GetReferencePathForRelativePaths().empty())
182
0
                {
183
0
                    osDatasetName = GDALDataset::BuildFilename(
184
0
                        osDatasetName.c_str(),
185
0
                        GetReferencePathForRelativePaths().c_str(), true);
186
0
                }
187
0
                aosInputDatasetNames.push_back(osDatasetName.c_str());
188
0
            }
189
0
        }
190
0
    }
191
0
    if (foundByName && foundByRef)
192
0
    {
193
0
        ReportError(CE_Failure, CPLE_NotSupported,
194
0
                    "Input datasets should be provided either all by reference "
195
0
                    "or all by name");
196
0
        return false;
197
0
    }
198
199
0
    return true;
200
0
}
201
202
/************************************************************************/
203
/*      GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions()      */
204
/************************************************************************/
205
206
void GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions(
207
    CPLStringList &aosOptions)
208
0
{
209
0
    const auto aosTokens =
210
0
        CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
211
0
    if (aosTokens.size() == 2)
212
0
    {
213
0
        aosOptions.push_back("-tr");
214
0
        aosOptions.push_back(aosTokens[0]);
215
0
        aosOptions.push_back(aosTokens[1]);
216
0
    }
217
0
    else
218
0
    {
219
0
        aosOptions.push_back("-resolution");
220
0
        aosOptions.push_back(m_resolution);
221
0
    }
222
223
0
    if (!m_bbox.empty())
224
0
    {
225
0
        aosOptions.push_back("-te");
226
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
227
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
228
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
229
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
230
0
    }
231
0
    if (m_targetAlignedPixels)
232
0
    {
233
0
        aosOptions.push_back("-tap");
234
0
    }
235
0
    if (!m_srcNoData.empty())
236
0
    {
237
0
        aosOptions.push_back("-srcnodata");
238
0
        std::string s;
239
0
        for (double v : m_srcNoData)
240
0
        {
241
0
            if (!s.empty())
242
0
                s += " ";
243
0
            s += CPLSPrintf("%.17g", v);
244
0
        }
245
0
        aosOptions.push_back(s);
246
0
    }
247
0
    if (!m_dstNoData.empty())
248
0
    {
249
0
        aosOptions.push_back("-vrtnodata");
250
0
        std::string s;
251
0
        for (double v : m_dstNoData)
252
0
        {
253
0
            if (!s.empty())
254
0
                s += " ";
255
0
            s += CPLSPrintf("%.17g", v);
256
0
        }
257
0
        aosOptions.push_back(s);
258
0
    }
259
0
    for (const int b : m_bands)
260
0
    {
261
0
        aosOptions.push_back("-b");
262
0
        aosOptions.push_back(CPLSPrintf("%d", b));
263
0
    }
264
0
    if (m_hideNoData)
265
0
    {
266
0
        aosOptions.push_back("-hidenodata");
267
0
    }
268
0
    if (m_writeAbsolutePaths)
269
0
    {
270
0
        aosOptions.push_back("-write_absolute_path");
271
0
    }
272
0
}
273
274
/************************************************************************/
275
/*           GDALRasterMosaicStackCommonAlgorithm::RunImpl()            */
276
/************************************************************************/
277
278
bool GDALRasterMosaicStackCommonAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
279
                                                   void *pProgressData)
280
0
{
281
0
    if (m_standaloneStep)
282
0
    {
283
0
        GDALRasterWriteAlgorithm writeAlg;
284
0
        for (auto &arg : writeAlg.GetArgs())
285
0
        {
286
0
            if (!arg->IsHidden())
287
0
            {
288
0
                auto stepArg = GetArg(arg->GetName());
289
0
                if (stepArg && stepArg->IsExplicitlySet())
290
0
                {
291
0
                    arg->SetSkipIfAlreadySet(true);
292
0
                    arg->SetFrom(*stepArg);
293
0
                }
294
0
            }
295
0
        }
296
297
        // Already checked by GDALAlgorithm::Run()
298
0
        CPLAssert(!m_executionForStreamOutput ||
299
0
                  EQUAL(m_format.c_str(), "stream"));
300
301
0
        m_standaloneStep = false;
302
0
        bool ret = Run(pfnProgress, pProgressData);
303
0
        m_standaloneStep = true;
304
0
        if (ret)
305
0
        {
306
0
            if (m_format == "stream")
307
0
            {
308
0
                ret = true;
309
0
            }
310
0
            else
311
0
            {
312
0
                std::vector<GDALArgDatasetValue> inputDataset(1);
313
0
                inputDataset[0].Set(m_outputDataset.GetDatasetRef());
314
0
                auto inputArg = writeAlg.GetArg(GDAL_ARG_NAME_INPUT);
315
0
                CPLAssert(inputArg);
316
0
                inputArg->Set(std::move(inputDataset));
317
0
                if (writeAlg.Run(pfnProgress, pProgressData))
318
0
                {
319
0
                    m_outputDataset.Set(
320
0
                        writeAlg.m_outputDataset.GetDatasetRef());
321
0
                    ret = true;
322
0
                }
323
0
            }
324
0
        }
325
326
0
        return ret;
327
0
    }
328
0
    else
329
0
    {
330
0
        GDALPipelineStepRunContext stepCtxt;
331
0
        stepCtxt.m_pfnProgress = pfnProgress;
332
0
        stepCtxt.m_pProgressData = pProgressData;
333
0
        return RunStep(stepCtxt);
334
0
    }
335
0
}
336
337
//! @endcond