Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_select.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "select" step of "raster pipeline"
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_select.h"
14
15
#include "gdal_priv.h"
16
#include "gdal_utils.h"
17
18
#include <map>
19
#include <set>
20
21
//! @cond Doxygen_Suppress
22
23
#ifndef _
24
0
#define _(x) (x)
25
#endif
26
27
/************************************************************************/
28
/*        GDALRasterSelectAlgorithm::GDALRasterSelectAlgorithm()        */
29
/************************************************************************/
30
31
GDALRasterSelectAlgorithm::GDALRasterSelectAlgorithm(bool standaloneStep)
32
0
    : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
33
0
                                      standaloneStep)
34
0
{
35
0
    {
36
0
        auto &arg = AddArg("band", 'b',
37
0
                           _("Band(s) (1-based index, 'mask', 'mask:<band>' or "
38
0
                             "color interpretation such as 'red')"),
39
0
                           &m_bands)
40
0
                        .SetPositional()
41
0
                        .SetRequired()
42
0
                        .SetMinCount(1);
43
0
        arg.SetAutoCompleteFunction(
44
0
            [this](const std::string &)
45
0
            {
46
0
                std::vector<std::string> ret;
47
0
                std::unique_ptr<GDALDataset> poSrcDSTmp;
48
0
                GDALDataset *poSrcDS = m_inputDataset.empty()
49
0
                                           ? nullptr
50
0
                                           : m_inputDataset[0].GetDatasetRef();
51
0
                if (!poSrcDS && !m_inputDataset.empty())
52
0
                {
53
0
                    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
54
0
                    poSrcDSTmp.reset(GDALDataset::Open(
55
0
                        m_inputDataset[0].GetName().c_str(), GDAL_OF_RASTER));
56
0
                    poSrcDS = poSrcDSTmp.get();
57
0
                }
58
0
                if (poSrcDS)
59
0
                {
60
0
                    std::set<GDALColorInterp> oSetColorInterp;
61
0
                    for (int i = 1; i <= poSrcDS->GetRasterCount(); ++i)
62
0
                    {
63
0
                        ret.push_back(std::to_string(i));
64
0
                        oSetColorInterp.insert(poSrcDS->GetRasterBand(i)
65
0
                                                   ->GetColorInterpretation());
66
0
                    }
67
0
                    ret.push_back("mask");
68
0
                    for (const auto eColorInterp : oSetColorInterp)
69
0
                    {
70
0
                        ret.push_back(CPLString(GDALGetColorInterpretationName(
71
0
                                                    eColorInterp))
72
0
                                          .tolower());
73
0
                    }
74
0
                }
75
0
                return ret;
76
0
            });
77
0
        arg.AddValidationAction(
78
0
            [&arg]()
79
0
            {
80
0
                int nColorInterpretations = 0;
81
0
                const auto paeColorInterp =
82
0
                    GDALGetColorInterpretationList(&nColorInterpretations);
83
0
                std::set<std::string> oSetValidColorInterp;
84
0
                for (int i = 0; i < nColorInterpretations; ++i)
85
0
                    oSetValidColorInterp.insert(
86
0
                        CPLString(
87
0
                            GDALGetColorInterpretationName(paeColorInterp[i]))
88
0
                            .tolower());
89
90
0
                const auto &val = arg.Get<std::vector<std::string>>();
91
0
                for (const auto &v : val)
92
0
                {
93
0
                    if (!STARTS_WITH(v.c_str(), "mask") &&
94
0
                        !(CPLGetValueType(v.c_str()) == CPL_VALUE_INTEGER &&
95
0
                          atoi(v.c_str()) >= 1) &&
96
0
                        !cpl::contains(oSetValidColorInterp,
97
0
                                       CPLString(v).tolower()))
98
0
                    {
99
0
                        CPLError(CE_Failure, CPLE_AppDefined,
100
0
                                 "Invalid band specification.");
101
0
                        return false;
102
0
                    }
103
0
                }
104
0
                return true;
105
0
            });
106
0
    }
107
108
0
    AddArg("exclude", 0, _("Exclude specified bands"), &m_exclude);
109
110
0
    {
111
0
        auto &arg = AddArg(
112
0
            "mask", 0,
113
0
            _("Mask band (1-based index, 'mask', 'mask:<band>' or 'none')"),
114
0
            &m_mask);
115
0
        arg.AddValidationAction(
116
0
            [&arg]()
117
0
            {
118
0
                const auto &v = arg.Get<std::string>();
119
0
                if (!STARTS_WITH(v.c_str(), "mask") &&
120
0
                    !EQUAL(v.c_str(), "none") &&
121
0
                    !(CPLGetValueType(v.c_str()) == CPL_VALUE_INTEGER &&
122
0
                      atoi(v.c_str()) >= 1))
123
0
                {
124
0
                    CPLError(CE_Failure, CPLE_AppDefined,
125
0
                             "Invalid mask band specification.");
126
0
                    return false;
127
0
                }
128
0
                return true;
129
0
            });
130
0
    }
131
0
}
132
133
/************************************************************************/
134
/*                 GDALRasterSelectAlgorithm::RunStep()                 */
135
/************************************************************************/
136
137
bool GDALRasterSelectAlgorithm::RunStep(GDALPipelineStepRunContext &)
138
0
{
139
0
    const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
140
0
    CPLAssert(poSrcDS);
141
0
    CPLAssert(m_outputDataset.GetName().empty());
142
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
143
144
0
    std::map<GDALColorInterp, std::vector<int>> oMapColorInterpToBands;
145
0
    for (int i = 1; i <= poSrcDS->GetRasterCount(); ++i)
146
0
    {
147
0
        oMapColorInterpToBands[poSrcDS->GetRasterBand(i)
148
0
                                   ->GetColorInterpretation()]
149
0
            .push_back(i);
150
0
    }
151
152
0
    CPLStringList aosOptions;
153
0
    aosOptions.AddString("-of");
154
0
    aosOptions.AddString("VRT");
155
0
    if (m_exclude)
156
0
    {
157
0
        if (m_bands.size() >= static_cast<size_t>(poSrcDS->GetRasterCount()))
158
0
        {
159
0
            ReportError(CE_Failure, CPLE_AppDefined,
160
0
                        "Cannot exclude all input bands");
161
0
            return false;
162
0
        }
163
164
0
        std::set<int> excludedBandsFromColor;
165
0
        for (const std::string &v : m_bands)
166
0
        {
167
0
            const auto eColorInterp =
168
0
                GDALGetColorInterpretationByName(v.c_str());
169
0
            if (v == "undefined" || eColorInterp != GCI_Undefined)
170
0
            {
171
0
                const auto iter = oMapColorInterpToBands.find(eColorInterp);
172
0
                if (iter != oMapColorInterpToBands.end())
173
0
                {
174
0
                    for (const int iBand : iter->second)
175
0
                    {
176
0
                        excludedBandsFromColor.insert(iBand);
177
0
                    }
178
0
                }
179
                // We don't emit a warning if there are no bands matching
180
                // the color interpretation, because a potential use case
181
                // could be to run on a set of input files that might have or
182
                // might not have an alpha band, and remove it.
183
0
            }
184
0
        }
185
186
0
        for (int i = 1; i <= poSrcDS->GetRasterCount(); ++i)
187
0
        {
188
0
            const std::string iStr = std::to_string(i);
189
0
            if (std::find(m_bands.begin(), m_bands.end(), iStr) ==
190
0
                    m_bands.end() &&
191
0
                !cpl::contains(excludedBandsFromColor, i))
192
0
            {
193
0
                aosOptions.AddString("-b");
194
0
                aosOptions.AddString(iStr);
195
0
            }
196
0
        }
197
0
    }
198
0
    else
199
0
    {
200
0
        for (const std::string &v : m_bands)
201
0
        {
202
0
            const auto eColorInterp =
203
0
                GDALGetColorInterpretationByName(v.c_str());
204
0
            if (v == "undefined" || eColorInterp != GCI_Undefined)
205
0
            {
206
0
                const auto iter = oMapColorInterpToBands.find(eColorInterp);
207
0
                if (iter == oMapColorInterpToBands.end())
208
0
                {
209
0
                    ReportError(CE_Failure, CPLE_AppDefined,
210
0
                                "No band has color interpretation %s",
211
0
                                v.c_str());
212
0
                    return false;
213
0
                }
214
0
                for (const int iBand : iter->second)
215
0
                {
216
0
                    aosOptions.AddString("-b");
217
0
                    aosOptions.AddString(std::to_string(iBand));
218
0
                }
219
0
            }
220
0
            else
221
0
            {
222
0
                aosOptions.AddString("-b");
223
0
                aosOptions.AddString(CPLString(v).replaceAll(':', ',').c_str());
224
0
            }
225
0
        }
226
0
    }
227
0
    if (!m_mask.empty())
228
0
    {
229
0
        aosOptions.AddString("-mask");
230
0
        aosOptions.AddString(CPLString(m_mask).replaceAll(':', ',').c_str());
231
0
    }
232
233
0
    GDALTranslateOptions *psOptions =
234
0
        GDALTranslateOptionsNew(aosOptions.List(), nullptr);
235
236
0
    auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
237
0
        GDALTranslate("", GDALDataset::ToHandle(poSrcDS), psOptions, nullptr)));
238
0
    GDALTranslateOptionsFree(psOptions);
239
0
    const bool bRet = poOutDS != nullptr;
240
0
    if (poOutDS)
241
0
    {
242
0
        m_outputDataset.Set(std::move(poOutDS));
243
0
    }
244
245
0
    return bRet;
246
0
}
247
248
0
GDALRasterSelectAlgorithmStandalone::~GDALRasterSelectAlgorithmStandalone() =
249
    default;
250
251
//! @endcond