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 107 : 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 107 : 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 34 : bool IsNativelyStreamingCompatible() const override 71 : { 72 34 : return false; 73 : } 74 : 75 21 : bool CanBeMiddleStep() const override 76 : { 77 21 : return true; 78 : } 79 : 80 23 : bool CanBeLastStep() const override 81 : { 82 23 : 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 4 : int GetInputType() const override 99 : { 100 4 : return nDatasetType; 101 : } 102 : 103 20 : int GetOutputType() const override 104 : { 105 20 : 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 107 : GDALTeeStepAlgorithmBase<BaseStepAlgorithm, 118 : nDatasetType>::GDALTeeStepAlgorithmBase() 119 : : BaseStepAlgorithm(NAME, DESCRIPTION, HELP_URL, 120 : GDALPipelineStepAlgorithm::ConstructorOptions() 121 107 : .SetAddDefaultArguments(false)) 122 : { 123 107 : this->AddInputDatasetArg(&this->m_inputDataset, 0, true).SetHidden(); 124 : 125 107 : this->AddArg("tee-pipeline", 0, _("Nested pipeline"), &m_pipelines, 126 : nDatasetType) 127 214 : .SetPositional() 128 107 : .SetMinCount(1) 129 107 : .SetMaxCount(INT_MAX) 130 107 : .SetMetaVar("PIPELINE") 131 214 : .SetPackedValuesAllowed(false) 132 107 : .SetDatasetInputFlags(GADV_NAME) 133 107 : .SetDatasetOutputFlags(GADV_NAME) 134 107 : .SetAutoOpenDataset(false); 135 107 : } 136 : 137 : /************************************************************************/ 138 : /* GDALTeeStepAlgorithmBase::RunStep() */ 139 : /************************************************************************/ 140 : 141 : template <class BaseStepAlgorithm, int nDatasetType> 142 16 : bool GDALTeeStepAlgorithmBase<BaseStepAlgorithm, nDatasetType>::RunStep( 143 : GDALPipelineStepRunContext &ctxt) 144 : { 145 16 : auto pfnProgress = ctxt.m_pfnProgress; 146 16 : auto pProgressData = ctxt.m_pProgressData; 147 : 148 16 : auto poSrcDS = this->m_inputDataset[0].GetDatasetRef(); 149 16 : CPLAssert(poSrcDS); 150 16 : CPLAssert(this->m_outputDataset.GetName().empty()); 151 16 : CPLAssert(!this->m_outputDataset.GetDatasetRef()); 152 : 153 : // Backup filters 154 32 : std::vector<std::string> aosAttributeFilters; 155 32 : std::vector<std::unique_ptr<OGRGeometry>> apoSpatialFilters; 156 20 : for (auto *poLayer : poSrcDS->GetLayers()) 157 : { 158 4 : const char *pszQueryString = poLayer->GetAttrQueryString(); 159 4 : aosAttributeFilters.push_back(pszQueryString ? pszQueryString : ""); 160 4 : const auto poSpatFilter = poLayer->GetSpatialFilter(); 161 4 : apoSpatialFilters.push_back(std::unique_ptr<OGRGeometry>( 162 0 : poSpatFilter ? poSpatFilter->clone() : nullptr)); 163 : } 164 : 165 16 : int iTeeDS = 0; 166 31 : for (const auto &dataset : m_pipelines) 167 : { 168 21 : const auto oIter = m_oMapNameToAlg.find(dataset.GetName()); 169 21 : 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 20 : auto subAlg = oIter->second.first; 177 20 : const auto &subAlgArgs = oIter->second.second; 178 : 179 20 : auto &subAlgInputDatasets = subAlg->GetInputDatasets(); 180 20 : CPLAssert(subAlgInputDatasets.empty()); 181 20 : subAlgInputDatasets.resize(1); 182 20 : subAlgInputDatasets[0].Set(poSrcDS); 183 : 184 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> 185 20 : pScaledProgress( 186 : GDALCreateScaledProgress( 187 20 : double(iTeeDS) / static_cast<int>(m_pipelines.size()), 188 20 : double(iTeeDS + 1) / static_cast<int>(m_pipelines.size()), 189 : pfnProgress, pProgressData), 190 20 : GDALDestroyScaledProgress); 191 : 192 20 : if (this->IsCalledFromCommandLine()) 193 3 : subAlg->SetCalledFromCommandLine(); 194 : 195 20 : bool ret = (subAlg->ParseCommandLineArguments(subAlgArgs) && 196 15 : subAlg->Run(pScaledProgress ? GDALScaledProgress : nullptr, 197 35 : pScaledProgress.get()) && 198 15 : subAlg->Finalize()); 199 : 200 20 : this->m_output += subAlg->GetOutputString(); 201 : 202 : // Restore filters 203 25 : for (int i = 0; i < static_cast<int>(aosAttributeFilters.size()); ++i) 204 : { 205 5 : auto poLayer = poSrcDS->GetLayer(i); 206 10 : poLayer->SetAttributeFilter(aosAttributeFilters[i].empty() 207 5 : ? aosAttributeFilters[i].c_str() 208 : : nullptr); 209 5 : poLayer->SetSpatialFilter(apoSpatialFilters[i].get()); 210 5 : poLayer->ResetReading(); 211 : } 212 : 213 20 : if (!ret) 214 5 : return false; 215 : 216 15 : ++iTeeDS; 217 : } 218 : 219 10 : this->m_outputDataset.Set(poSrcDS); 220 10 : return true; 221 : } 222 : 223 : /************************************************************************/ 224 : /* GDALTeeRasterAlgorithm */ 225 : /************************************************************************/ 226 : 227 132 : class GDALTeeRasterAlgorithm final 228 : : public GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 229 : GDAL_OF_RASTER> 230 : { 231 : public: 232 66 : GDALTeeRasterAlgorithm() = default; 233 : 234 : ~GDALTeeRasterAlgorithm() override; 235 : }; 236 : 237 : /************************************************************************/ 238 : /* GDALTeeVectorAlgorithm */ 239 : /************************************************************************/ 240 : 241 82 : class GDALTeeVectorAlgorithm final 242 : : public GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 243 : GDAL_OF_VECTOR> 244 : { 245 : public: 246 41 : GDALTeeVectorAlgorithm() = default; 247 : 248 : ~GDALTeeVectorAlgorithm() override; 249 : }; 250 : 251 : //! @endcond 252 : 253 : #endif