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 115 : 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 14 : void CopyFilenameBindingsFrom(const GDALTeeStepAlgorithmAbstract *other) 44 : { 45 14 : m_oMapNameToAlg = other->m_oMapNameToAlg; 46 14 : } 47 : 48 17 : bool BindFilename(const std::string &filename, 49 : GDALPipelineStepAlgorithm *alg, 50 : const std::vector<std::string> &args) 51 : { 52 17 : if (cpl::contains(m_oMapNameToAlg, filename)) 53 1 : return false; 54 16 : m_oMapNameToAlg[filename] = std::make_pair(alg, args); 55 16 : return true; 56 : } 57 : 58 : protected: 59 115 : GDALTeeStepAlgorithmAbstract() = default; 60 : 61 : std::vector<GDALArgDatasetValue> m_pipelines{}; 62 : std::map<std::string, 63 : std::pair<GDALPipelineStepAlgorithm *, std::vector<std::string>>> 64 : m_oMapNameToAlg{}; 65 : }; 66 : 67 : /************************************************************************/ 68 : /* GDALTeeStepAlgorithmBase */ 69 : /************************************************************************/ 70 : 71 : template <class BaseStepAlgorithm, int nDatasetType> 72 : class GDALTeeStepAlgorithmBase /* non final */ 73 : : public BaseStepAlgorithm, 74 : public GDALTeeStepAlgorithmAbstract 75 : { 76 : public: 77 25 : bool IsNativelyStreamingCompatible() const override 78 : { 79 25 : return false; 80 : } 81 : 82 28 : bool CanBeMiddleStep() const override 83 : { 84 28 : return true; 85 : } 86 : 87 49 : bool CanBeLastStep() const override 88 : { 89 49 : return true; 90 : } 91 : 92 1 : bool GeneratesFilesFromUserInput() const override 93 : { 94 1 : return true; 95 : } 96 : 97 : protected: 98 : explicit GDALTeeStepAlgorithmBase(); 99 : 100 4 : int GetInputType() const override 101 : { 102 4 : return nDatasetType; 103 : } 104 : 105 14 : int GetOutputType() const override 106 : { 107 14 : return nDatasetType; 108 : } 109 : 110 : private: 111 : bool RunStep(GDALPipelineStepRunContext &ctxt) override; 112 : }; 113 : 114 : /************************************************************************/ 115 : /* GDALTeeStepAlgorithmBase::GDALTeeStepAlgorithmBase() */ 116 : /************************************************************************/ 117 : 118 : template <class BaseStepAlgorithm, int nDatasetType> 119 115 : GDALTeeStepAlgorithmBase<BaseStepAlgorithm, 120 : nDatasetType>::GDALTeeStepAlgorithmBase() 121 : : BaseStepAlgorithm(NAME, DESCRIPTION, HELP_URL, 122 : GDALPipelineStepAlgorithm::ConstructorOptions() 123 115 : .SetAddDefaultArguments(false)) 124 : { 125 115 : this->AddArg("tee-pipeline", 0, _("Nested pipeline"), &m_pipelines, 126 : nDatasetType) 127 230 : .SetPositional() 128 115 : .SetMinCount(1) 129 115 : .SetMaxCount(INT_MAX) 130 115 : .SetMetaVar("PIPELINE") 131 230 : .SetPackedValuesAllowed(false) 132 115 : .SetDatasetInputFlags(GADV_NAME) 133 115 : .SetDatasetOutputFlags(GADV_NAME) 134 115 : .SetAutoOpenDataset(false); 135 115 : } 136 : 137 : /************************************************************************/ 138 : /* GDALTeeStepAlgorithmBase::RunStep() */ 139 : /************************************************************************/ 140 : 141 : template <class BaseStepAlgorithm, int nDatasetType> 142 12 : bool GDALTeeStepAlgorithmBase<BaseStepAlgorithm, nDatasetType>::RunStep( 143 : GDALPipelineStepRunContext &ctxt) 144 : { 145 12 : auto pfnProgress = ctxt.m_pfnProgress; 146 12 : auto pProgressData = ctxt.m_pProgressData; 147 : 148 12 : auto poSrcDS = this->m_inputDataset[0].GetDatasetRef(); 149 12 : CPLAssert(poSrcDS); 150 12 : CPLAssert(this->m_outputDataset.GetName().empty()); 151 12 : CPLAssert(!this->m_outputDataset.GetDatasetRef()); 152 : 153 : // Backup filters 154 24 : std::vector<std::string> aosAttributeFilters; 155 24 : std::vector<std::unique_ptr<OGRGeometry>> apoSpatialFilters; 156 16 : 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 12 : int iTeeDS = 0; 166 20 : for (const auto &dataset : m_pipelines) 167 : { 168 14 : const auto oIter = m_oMapNameToAlg.find(dataset.GetName()); 169 14 : 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 13 : auto subAlg = oIter->second.first; 177 13 : const auto &subAlgArgs = oIter->second.second; 178 : 179 13 : auto &subAlgInputDatasets = subAlg->GetInputDatasets(); 180 13 : CPLAssert(subAlgInputDatasets.empty()); 181 13 : subAlgInputDatasets.resize(1); 182 13 : subAlgInputDatasets[0].Set(poSrcDS); 183 : 184 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> 185 13 : pScaledProgress( 186 : GDALCreateScaledProgress( 187 13 : double(iTeeDS) / static_cast<int>(m_pipelines.size()), 188 13 : double(iTeeDS + 1) / static_cast<int>(m_pipelines.size()), 189 : pfnProgress, pProgressData), 190 13 : GDALDestroyScaledProgress); 191 : 192 13 : bool ret = (subAlg->ParseCommandLineArguments(subAlgArgs) && 193 8 : subAlg->Run(pScaledProgress ? GDALScaledProgress : nullptr, 194 21 : pScaledProgress.get()) && 195 8 : subAlg->Finalize()); 196 : 197 : // Restore filters 198 18 : for (int i = 0; i < static_cast<int>(aosAttributeFilters.size()); ++i) 199 : { 200 5 : auto poLayer = poSrcDS->GetLayer(i); 201 10 : poLayer->SetAttributeFilter(aosAttributeFilters[i].empty() 202 5 : ? aosAttributeFilters[i].c_str() 203 : : nullptr); 204 5 : poLayer->SetSpatialFilter(apoSpatialFilters[i].get()); 205 5 : poLayer->ResetReading(); 206 : } 207 : 208 13 : if (!ret) 209 5 : return false; 210 : 211 8 : ++iTeeDS; 212 : } 213 : 214 6 : this->m_outputDataset.Set(poSrcDS); 215 6 : return true; 216 : } 217 : 218 : /************************************************************************/ 219 : /* GDALTeeRasterAlgorithm */ 220 : /************************************************************************/ 221 : 222 132 : class GDALTeeRasterAlgorithm final 223 : : public GDALTeeStepAlgorithmBase<GDALRasterPipelineStepAlgorithm, 224 : GDAL_OF_RASTER> 225 : { 226 : public: 227 66 : GDALTeeRasterAlgorithm() = default; 228 : 229 : ~GDALTeeRasterAlgorithm() override; 230 : }; 231 : 232 : /************************************************************************/ 233 : /* GDALTeeVectorAlgorithm */ 234 : /************************************************************************/ 235 : 236 98 : class GDALTeeVectorAlgorithm final 237 : : public GDALTeeStepAlgorithmBase<GDALVectorPipelineStepAlgorithm, 238 : GDAL_OF_VECTOR> 239 : { 240 : public: 241 49 : GDALTeeVectorAlgorithm() = default; 242 : 243 : ~GDALTeeVectorAlgorithm() override; 244 : }; 245 : 246 : //! @endcond 247 : 248 : #endif