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->AddInputDatasetArg(&this->m_inputDataset, 0, true).SetHidden();
124
125 this->AddArg("tee-pipeline", 0, _("Nested pipeline"), &m_pipelines,
126 nDatasetType)
127 .SetPositional()
128 .SetMinCount(1)
129 .SetMaxCount(INT_MAX)
130 .SetMetaVar("PIPELINE")
131 .SetPackedValuesAllowed(false)
132 .SetDatasetInputFlags(GADV_NAME)
133 .SetDatasetOutputFlags(GADV_NAME)
134 .SetAutoOpenDataset(false);
135}
136
137/************************************************************************/
138/* GDALTeeStepAlgorithmBase::RunStep() */
139/************************************************************************/
140
141template <class BaseStepAlgorithm, int nDatasetType>
142bool GDALTeeStepAlgorithmBase<BaseStepAlgorithm, nDatasetType>::RunStep(
143 GDALPipelineStepRunContext &ctxt)
144{
145 auto pfnProgress = ctxt.m_pfnProgress;
146 auto pProgressData = ctxt.m_pProgressData;
147
148 auto poSrcDS = this->m_inputDataset[0].GetDatasetRef();
149 CPLAssert(poSrcDS);
150 CPLAssert(this->m_outputDataset.GetName().empty());
151 CPLAssert(!this->m_outputDataset.GetDatasetRef());
152
153 // Backup filters
154 std::vector<std::string> aosAttributeFilters;
155 std::vector<std::unique_ptr<OGRGeometry>> apoSpatialFilters;
156 for (auto *poLayer : poSrcDS->GetLayers())
157 {
158 const char *pszQueryString = poLayer->GetAttrQueryString();
159 aosAttributeFilters.push_back(pszQueryString ? pszQueryString : "");
160 const auto poSpatFilter = poLayer->GetSpatialFilter();
161 apoSpatialFilters.push_back(std::unique_ptr<OGRGeometry>(
162 poSpatFilter ? poSpatFilter->clone() : nullptr));
163 }
164
165 int iTeeDS = 0;
166 for (const auto &dataset : m_pipelines)
167 {
168 const auto oIter = m_oMapNameToAlg.find(dataset.GetName());
169 if (oIter == m_oMapNameToAlg.end())
170 {
171 this->ReportError(CE_Failure, CPLE_AppDefined,
172 "'%s' is not a valid nested pipeline",
173 dataset.GetName().c_str());
174 return false;
175 }
176 auto subAlg = oIter->second.first;
177 const auto &subAlgArgs = oIter->second.second;
178
179 auto &subAlgInputDatasets = subAlg->GetInputDatasets();
180 CPLAssert(subAlgInputDatasets.empty());
181 subAlgInputDatasets.resize(1);
182 subAlgInputDatasets[0].Set(poSrcDS);
183
184 std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
185 pScaledProgress(
186 GDALCreateScaledProgress(
187 double(iTeeDS) / static_cast<int>(m_pipelines.size()),
188 double(iTeeDS + 1) / static_cast<int>(m_pipelines.size()),
189 pfnProgress, pProgressData),
190 GDALDestroyScaledProgress);
191
192 if (this->IsCalledFromCommandLine())
193 subAlg->SetCalledFromCommandLine();
194
195 bool ret = (subAlg->ParseCommandLineArguments(subAlgArgs) &&
196 subAlg->Run(pScaledProgress ? GDALScaledProgress : nullptr,
197 pScaledProgress.get()) &&
198 subAlg->Finalize());
199
200 this->m_output += subAlg->GetOutputString();
201
202 // Restore filters
203 for (int i = 0; i < static_cast<int>(aosAttributeFilters.size()); ++i)
204 {
205 auto poLayer = poSrcDS->GetLayer(i);
206 poLayer->SetAttributeFilter(aosAttributeFilters[i].empty()
207 ? aosAttributeFilters[i].c_str()
208 : nullptr);
209 poLayer->SetSpatialFilter(apoSpatialFilters[i].get());
210 poLayer->ResetReading();
211 }
212
213 if (!ret)
214 return false;
215
216 ++iTeeDS;
217 }
218
219 this->m_outputDataset.Set(poSrcDS);
220 return true;
221}
222
223/************************************************************************/
224/* GDALTeeRasterAlgorithm */
225/************************************************************************/
226
227class GDALTeeRasterAlgorithm final
228 : public GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm,
229 GDAL_OF_RASTER>
230{
231 public:
232 GDALTeeRasterAlgorithm() = default;
233
234 ~GDALTeeRasterAlgorithm() override;
235};
236
237/************************************************************************/
238/* GDALTeeVectorAlgorithm */
239/************************************************************************/
240
241class GDALTeeVectorAlgorithm final
242 : public GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm,
243 GDAL_OF_VECTOR>
244{
245 public:
246 GDALTeeVectorAlgorithm() = default;
247
248 ~GDALTeeVectorAlgorithm() override;
249};
250
252
253#endif
#define CPLAssert(expr)
Assert on an expression.
Definition cpl_error.h:353
@ CE_Failure
Error that prevents the current operation to succeed.
Definition cpl_error.h:60
#define CPLE_AppDefined
Application defined error.
Definition cpl_error.h:108
Classes related to registration of format support, and opening datasets.