GDAL
gdalalg_tee.h
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
24
25#ifndef _
26#define _(x) (x)
27#endif
28
29/************************************************************************/
30/* GDALTeeStepAlgorithmAbstract */
31/************************************************************************/
32
33class 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 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
64template <class BaseStepAlgorithm, int nDatasetType>
65class GDALTeeStepAlgorithmBase /* non final */
66 : public BaseStepAlgorithm,
67 public GDALTeeStepAlgorithmAbstract
68{
69 public:
70 bool IsNativelyStreamingCompatible() const override
71 {
72 return false;
73 }
74
75 bool CanBeMiddleStep() const override
76 {
77 return true;
78 }
79
80 bool CanBeLastStep() const override
81 {
82 return true;
83 }
84
85 bool GeneratesFilesFromUserInput() const override
86 {
87 return true;
88 }
89
90 bool HasOutputString() const override
91 {
92 return GDALTeeStepAlgorithmAbstract::HasOutputString();
93 }
94
95 protected:
96 explicit GDALTeeStepAlgorithmBase();
97
98 int GetInputType() const override
99 {
100 return nDatasetType;
101 }
102
103 int GetOutputType() const override
104 {
105 return nDatasetType;
106 }
107
108 private:
109 bool RunStep(GDALPipelineStepRunContext &ctxt) override;
110};
111
112/************************************************************************/
113/* GDALTeeStepAlgorithmBase::GDALTeeStepAlgorithmBase() */
114/************************************************************************/
115
116template <class BaseStepAlgorithm, int nDatasetType>
117GDALTeeStepAlgorithmBase<BaseStepAlgorithm,
118 nDatasetType>::GDALTeeStepAlgorithmBase()
119 : BaseStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
120 GDALPipelineStepAlgorithm::ConstructorOptions()
121 .SetAddDefaultArguments(false))
122{
123 this->AddArg("tee-pipeline", 0, _("Nested pipeline"), &m_pipelines,
124 nDatasetType)
125 .SetPositional()
126 .SetMinCount(1)
127 .SetMaxCount(INT_MAX)
128 .SetMetaVar("PIPELINE")
129 .SetPackedValuesAllowed(false)
130 .SetDatasetInputFlags(GADV_NAME)
131 .SetDatasetOutputFlags(GADV_NAME)
132 .SetAutoOpenDataset(false);
133}
134
135/************************************************************************/
136/* GDALTeeStepAlgorithmBase::RunStep() */
137/************************************************************************/
138
139template <class BaseStepAlgorithm, int nDatasetType>
140bool GDALTeeStepAlgorithmBase<BaseStepAlgorithm, nDatasetType>::RunStep(
141 GDALPipelineStepRunContext &ctxt)
142{
143 auto pfnProgress = ctxt.m_pfnProgress;
144 auto pProgressData = ctxt.m_pProgressData;
145
146 auto poSrcDS = this->m_inputDataset[0].GetDatasetRef();
147 CPLAssert(poSrcDS);
148 CPLAssert(this->m_outputDataset.GetName().empty());
149 CPLAssert(!this->m_outputDataset.GetDatasetRef());
150
151 // Backup filters
152 std::vector<std::string> aosAttributeFilters;
153 std::vector<std::unique_ptr<OGRGeometry>> apoSpatialFilters;
154 for (auto *poLayer : poSrcDS->GetLayers())
155 {
156 const char *pszQueryString = poLayer->GetAttrQueryString();
157 aosAttributeFilters.push_back(pszQueryString ? pszQueryString : "");
158 const auto poSpatFilter = poLayer->GetSpatialFilter();
159 apoSpatialFilters.push_back(std::unique_ptr<OGRGeometry>(
160 poSpatFilter ? poSpatFilter->clone() : nullptr));
161 }
162
163 int iTeeDS = 0;
164 for (const auto &dataset : m_pipelines)
165 {
166 const auto oIter = m_oMapNameToAlg.find(dataset.GetName());
167 if (oIter == m_oMapNameToAlg.end())
168 {
169 this->ReportError(CE_Failure, CPLE_AppDefined,
170 "'%s' is not a valid nested pipeline",
171 dataset.GetName().c_str());
172 return false;
173 }
174 auto subAlg = oIter->second.first;
175 const auto &subAlgArgs = oIter->second.second;
176
177 auto &subAlgInputDatasets = subAlg->GetInputDatasets();
178 CPLAssert(subAlgInputDatasets.empty());
179 subAlgInputDatasets.resize(1);
180 subAlgInputDatasets[0].Set(poSrcDS);
181
182 std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
183 pScaledProgress(
184 GDALCreateScaledProgress(
185 double(iTeeDS) / static_cast<int>(m_pipelines.size()),
186 double(iTeeDS + 1) / static_cast<int>(m_pipelines.size()),
187 pfnProgress, pProgressData),
188 GDALDestroyScaledProgress);
189
190 if (this->IsCalledFromCommandLine())
191 subAlg->SetCalledFromCommandLine();
192
193 bool ret = (subAlg->ParseCommandLineArguments(subAlgArgs) &&
194 subAlg->Run(pScaledProgress ? GDALScaledProgress : nullptr,
195 pScaledProgress.get()) &&
196 subAlg->Finalize());
197
198 this->m_output += subAlg->GetOutputString();
199
200 // Restore filters
201 for (int i = 0; i < static_cast<int>(aosAttributeFilters.size()); ++i)
202 {
203 auto poLayer = poSrcDS->GetLayer(i);
204 poLayer->SetAttributeFilter(aosAttributeFilters[i].empty()
205 ? aosAttributeFilters[i].c_str()
206 : nullptr);
207 poLayer->SetSpatialFilter(apoSpatialFilters[i].get());
208 poLayer->ResetReading();
209 }
210
211 if (!ret)
212 return false;
213
214 ++iTeeDS;
215 }
216
217 this->m_outputDataset.Set(poSrcDS);
218 return true;
219}
220
221/************************************************************************/
222/* GDALTeeRasterAlgorithm */
223/************************************************************************/
224
225class GDALTeeRasterAlgorithm final
226 : public GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm,
227 GDAL_OF_RASTER>
228{
229 public:
230 GDALTeeRasterAlgorithm() = default;
231
232 ~GDALTeeRasterAlgorithm() override;
233};
234
235/************************************************************************/
236/* GDALTeeVectorAlgorithm */
237/************************************************************************/
238
239class GDALTeeVectorAlgorithm final
240 : public GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm,
241 GDAL_OF_VECTOR>
242{
243 public:
244 GDALTeeVectorAlgorithm() = default;
245
246 ~GDALTeeVectorAlgorithm() override;
247};
248
250
251#endif
#define CPLAssert(expr)
Assert on an expression.
Definition cpl_error.h:330
#define CPLE_AppDefined
Application defined error.
Definition cpl_error.h:85
Classes related to registration of format support, and opening datasets.