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