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