Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_tee.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "tee" pipeline step
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
#ifndef GDALALG_TEE_INCLUDED
14
#define GDALALG_TEE_INCLUDED
15
16
#include "gdalalg_abstract_pipeline.h"
17
#include "gdalalg_raster_pipeline.h"
18
#include "gdalalg_vector_pipeline.h"
19
#include "ogrsf_frmts.h"
20
21
#include <utility>
22
23
//! @cond Doxygen_Suppress
24
25
#ifndef _
26
0
#define _(x) (x)
27
#endif
28
29
/************************************************************************/
30
/*                     GDALTeeStepAlgorithmAbstract                     */
31
/************************************************************************/
32
33
class GDALTeeStepAlgorithmAbstract /* non final */
34
{
35
  public:
36
    static constexpr const char *NAME = "tee";
37
    static constexpr const char *DESCRIPTION =
38
        "Pipes the input into the output stream and side nested pipelines.";
39
    static constexpr const char *HELP_URL = "/programs/gdal_pipeline.html";
40
41
    virtual ~GDALTeeStepAlgorithmAbstract();
42
43
    void CopyFilenameBindingsFrom(const GDALTeeStepAlgorithmAbstract *other);
44
45
    bool BindFilename(const std::string &filename,
46
                      GDALAbstractPipelineAlgorithm *alg,
47
                      const std::vector<std::string> &args);
48
49
    bool HasOutputString() const;
50
51
  protected:
52
0
    GDALTeeStepAlgorithmAbstract() = default;
53
54
    std::vector<GDALArgDatasetValue> m_pipelines{};
55
    std::map<std::string, std::pair<GDALAbstractPipelineAlgorithm *,
56
                                    std::vector<std::string>>>
57
        m_oMapNameToAlg{};
58
};
59
60
/************************************************************************/
61
/*                       GDALTeeStepAlgorithmBase                       */
62
/************************************************************************/
63
64
template <class BaseStepAlgorithm, int nDatasetType>
65
class GDALTeeStepAlgorithmBase /* non final */
66
    : public BaseStepAlgorithm,
67
      public GDALTeeStepAlgorithmAbstract
68
{
69
  public:
70
    bool IsNativelyStreamingCompatible() const override
71
0
    {
72
0
        return false;
73
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::IsNativelyStreamingCompatible() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::IsNativelyStreamingCompatible() const
74
75
    bool CanBeMiddleStep() const override
76
0
    {
77
0
        return true;
78
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::CanBeMiddleStep() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::CanBeMiddleStep() const
79
80
    bool CanBeLastStep() const override
81
0
    {
82
0
        return true;
83
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::CanBeLastStep() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::CanBeLastStep() const
84
85
    bool GeneratesFilesFromUserInput() const override
86
0
    {
87
0
        return true;
88
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::GeneratesFilesFromUserInput() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::GeneratesFilesFromUserInput() const
89
90
    bool HasOutputString() const override
91
0
    {
92
0
        return GDALTeeStepAlgorithmAbstract::HasOutputString();
93
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::HasOutputString() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::HasOutputString() const
94
95
  protected:
96
    explicit GDALTeeStepAlgorithmBase();
97
98
    int GetInputType() const override
99
0
    {
100
0
        return nDatasetType;
101
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::GetInputType() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::GetInputType() const
102
103
    int GetOutputType() const override
104
0
    {
105
0
        return nDatasetType;
106
0
    }
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::GetOutputType() const
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::GetOutputType() const
107
108
  private:
109
    bool RunStep(GDALPipelineStepRunContext &ctxt) override;
110
};
111
112
/************************************************************************/
113
/*         GDALTeeStepAlgorithmBase::GDALTeeStepAlgorithmBase()         */
114
/************************************************************************/
115
116
template <class BaseStepAlgorithm, int nDatasetType>
117
GDALTeeStepAlgorithmBase<BaseStepAlgorithm,
118
                         nDatasetType>::GDALTeeStepAlgorithmBase()
119
0
    : BaseStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
120
0
                        GDALPipelineStepAlgorithm::ConstructorOptions()
121
0
                            .SetAddDefaultArguments(false))
122
0
{
123
0
    this->AddInputDatasetArg(&this->m_inputDataset, 0, true).SetHidden();
124
125
0
    this->AddArg("tee-pipeline", 0, _("Nested pipeline"), &m_pipelines,
126
0
                 nDatasetType)
127
0
        .SetPositional()
128
0
        .SetMinCount(1)
129
0
        .SetMaxCount(INT_MAX)
130
0
        .SetMetaVar("PIPELINE")
131
0
        .SetPackedValuesAllowed(false)
132
0
        .SetDatasetInputFlags(GADV_NAME)
133
0
        .SetDatasetOutputFlags(GADV_NAME)
134
0
        .SetAutoOpenDataset(false);
135
0
}
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::GDALTeeStepAlgorithmBase()
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::GDALTeeStepAlgorithmBase()
136
137
/************************************************************************/
138
/*                 GDALTeeStepAlgorithmBase::RunStep()                  */
139
/************************************************************************/
140
141
template <class BaseStepAlgorithm, int nDatasetType>
142
bool GDALTeeStepAlgorithmBase<BaseStepAlgorithm, nDatasetType>::RunStep(
143
    GDALPipelineStepRunContext &ctxt)
144
0
{
145
0
    auto pfnProgress = ctxt.m_pfnProgress;
146
0
    auto pProgressData = ctxt.m_pProgressData;
147
148
0
    auto poSrcDS = this->m_inputDataset[0].GetDatasetRef();
149
0
    CPLAssert(poSrcDS);
150
0
    CPLAssert(this->m_outputDataset.GetName().empty());
151
0
    CPLAssert(!this->m_outputDataset.GetDatasetRef());
152
153
    // Backup filters
154
0
    std::vector<std::string> aosAttributeFilters;
155
0
    std::vector<std::unique_ptr<OGRGeometry>> apoSpatialFilters;
156
0
    for (auto *poLayer : poSrcDS->GetLayers())
157
0
    {
158
0
        const char *pszQueryString = poLayer->GetAttrQueryString();
159
0
        aosAttributeFilters.push_back(pszQueryString ? pszQueryString : "");
160
0
        const auto poSpatFilter = poLayer->GetSpatialFilter();
161
0
        apoSpatialFilters.push_back(std::unique_ptr<OGRGeometry>(
162
0
            poSpatFilter ? poSpatFilter->clone() : nullptr));
163
0
    }
164
165
0
    int iTeeDS = 0;
166
0
    for (const auto &dataset : m_pipelines)
167
0
    {
168
0
        const auto oIter = m_oMapNameToAlg.find(dataset.GetName());
169
0
        if (oIter == m_oMapNameToAlg.end())
170
0
        {
171
0
            this->ReportError(CE_Failure, CPLE_AppDefined,
172
0
                              "'%s' is not a valid nested pipeline",
173
0
                              dataset.GetName().c_str());
174
0
            return false;
175
0
        }
176
0
        auto subAlg = oIter->second.first;
177
0
        const auto &subAlgArgs = oIter->second.second;
178
179
0
        auto &subAlgInputDatasets = subAlg->GetInputDatasets();
180
0
        CPLAssert(subAlgInputDatasets.empty());
181
0
        subAlgInputDatasets.resize(1);
182
0
        subAlgInputDatasets[0].Set(poSrcDS);
183
184
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
185
0
            pScaledProgress(
186
0
                GDALCreateScaledProgress(
187
0
                    double(iTeeDS) / static_cast<int>(m_pipelines.size()),
188
0
                    double(iTeeDS + 1) / static_cast<int>(m_pipelines.size()),
189
0
                    pfnProgress, pProgressData),
190
0
                GDALDestroyScaledProgress);
191
192
0
        if (this->IsCalledFromCommandLine())
193
0
            subAlg->SetCalledFromCommandLine();
194
195
0
        bool ret = (subAlg->ParseCommandLineArguments(subAlgArgs) &&
196
0
                    subAlg->Run(pScaledProgress ? GDALScaledProgress : nullptr,
197
0
                                pScaledProgress.get()) &&
198
0
                    subAlg->Finalize());
199
200
0
        this->m_output += subAlg->GetOutputString();
201
202
        // Restore filters
203
0
        for (int i = 0; i < static_cast<int>(aosAttributeFilters.size()); ++i)
204
0
        {
205
0
            auto poLayer = poSrcDS->GetLayer(i);
206
0
            poLayer->SetAttributeFilter(aosAttributeFilters[i].empty()
207
0
                                            ? aosAttributeFilters[i].c_str()
208
0
                                            : nullptr);
209
0
            poLayer->SetSpatialFilter(apoSpatialFilters[i].get());
210
0
            poLayer->ResetReading();
211
0
        }
212
213
0
        if (!ret)
214
0
            return false;
215
216
0
        ++iTeeDS;
217
0
    }
218
219
0
    this->m_outputDataset.Set(poSrcDS);
220
0
    return true;
221
0
}
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 2>::RunStep(GDALPipelineStepRunContext&)
Unexecuted instantiation: GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 4>::RunStep(GDALPipelineStepRunContext&)
222
223
/************************************************************************/
224
/*                        GDALTeeRasterAlgorithm                        */
225
/************************************************************************/
226
227
class GDALTeeRasterAlgorithm final
228
    : public GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm,
229
                                      GDAL_OF_RASTER>
230
{
231
  public:
232
0
    GDALTeeRasterAlgorithm() = default;
233
234
    ~GDALTeeRasterAlgorithm() override;
235
};
236
237
/************************************************************************/
238
/*                        GDALTeeVectorAlgorithm                        */
239
/************************************************************************/
240
241
class GDALTeeVectorAlgorithm final
242
    : public GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm,
243
                                      GDAL_OF_VECTOR>
244
{
245
  public:
246
0
    GDALTeeVectorAlgorithm() = default;
247
248
    ~GDALTeeVectorAlgorithm() override;
249
};
250
251
//! @endcond
252
253
#endif