LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_concat.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 230 239 96.2 %
Date: 2025-10-21 22:35:35 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "vector concat" subcommand
       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             : #include "gdalalg_vector_concat.h"
      14             : #include "gdalalg_vector_write.h"
      15             : 
      16             : #include "cpl_conv.h"
      17             : #include "gdal_priv.h"
      18             : #include "gdal_utils.h"
      19             : #include "ogrsf_frmts.h"
      20             : 
      21             : #include "ogrlayerdecorator.h"
      22             : #include "ogrunionlayer.h"
      23             : #include "ogrwarpedlayer.h"
      24             : 
      25             : #include <algorithm>
      26             : #include <set>
      27             : 
      28             : //! @cond Doxygen_Suppress
      29             : 
      30             : #ifndef _
      31             : #define _(x) (x)
      32             : #endif
      33             : 
      34             : /************************************************************************/
      35             : /*        GDALVectorConcatAlgorithm::GDALVectorConcatAlgorithm()        */
      36             : /************************************************************************/
      37             : 
      38          64 : GDALVectorConcatAlgorithm::GDALVectorConcatAlgorithm(bool bStandalone)
      39             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      40           0 :                                       ConstructorOptions()
      41          64 :                                           .SetStandaloneStep(bStandalone)
      42          64 :                                           .SetInputDatasetMaxCount(INT_MAX)
      43          64 :                                           .SetAddOutputLayerNameArgument(false)
      44         128 :                                           .SetAutoOpenInputDatasets(false))
      45             : {
      46          64 :     if (!bStandalone)
      47             :     {
      48          36 :         AddVectorInputArgs(/* hiddenForCLI = */ false);
      49             :     }
      50             : 
      51             :     AddArg(
      52             :         "mode", 0,
      53             :         _("Determine the strategy to create output layers from source layers "),
      54         128 :         &m_mode)
      55          64 :         .SetChoices("merge-per-layer-name", "stack", "single")
      56          64 :         .SetDefault(m_mode);
      57             :     AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
      58             :            _("Name of the output vector layer (single mode), or template to "
      59             :              "name the output vector layers (stack mode)"),
      60          64 :            &m_layerNameTemplate);
      61             :     AddArg("source-layer-field-name", 0,
      62             :            _("Name of the new field to add to contain identificoncation of the "
      63             :              "source layer, with value determined from "
      64             :              "'source-layer-field-content'"),
      65          64 :            &m_sourceLayerFieldName);
      66             :     AddArg("source-layer-field-content", 0,
      67             :            _("A string, possibly using {AUTO_NAME}, {DS_NAME}, {DS_BASENAME}, "
      68             :              "{DS_INDEX}, {LAYER_NAME}, {LAYER_INDEX}"),
      69          64 :            &m_sourceLayerFieldContent);
      70             :     AddArg("field-strategy", 0,
      71             :            _("How to determine target fields from source fields"),
      72         128 :            &m_fieldStrategy)
      73          64 :         .SetChoices("union", "intersection")
      74          64 :         .SetDefault(m_fieldStrategy);
      75         128 :     AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs)
      76         128 :         .SetIsCRSArg()
      77          64 :         .AddHiddenAlias("s_srs");
      78         128 :     AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
      79         128 :         .SetIsCRSArg()
      80          64 :         .AddHiddenAlias("t_srs");
      81          64 : }
      82             : 
      83             : GDALVectorConcatAlgorithm::~GDALVectorConcatAlgorithm() = default;
      84             : 
      85             : /************************************************************************/
      86             : /*                   GDALVectorConcatOutputDataset                      */
      87             : /************************************************************************/
      88             : 
      89             : class GDALVectorConcatOutputDataset final : public GDALDataset
      90             : {
      91             :     std::vector<std::unique_ptr<OGRLayer>> m_layers{};
      92             : 
      93             :   public:
      94          26 :     GDALVectorConcatOutputDataset() = default;
      95             : 
      96          33 :     void AddLayer(std::unique_ptr<OGRLayer> layer)
      97             :     {
      98          33 :         m_layers.push_back(std::move(layer));
      99          33 :     }
     100             : 
     101             :     int GetLayerCount() const override;
     102             : 
     103          56 :     OGRLayer *GetLayer(int idx) const override
     104             :     {
     105          56 :         return idx >= 0 && idx < GetLayerCount() ? m_layers[idx].get()
     106          56 :                                                  : nullptr;
     107             :     }
     108             : 
     109          31 :     int TestCapability(const char *pszCap) const override
     110             :     {
     111          31 :         if (EQUAL(pszCap, ODsCCurveGeometries) ||
     112          29 :             EQUAL(pszCap, ODsCMeasuredGeometries) ||
     113          28 :             EQUAL(pszCap, ODsCZGeometries))
     114             :         {
     115           4 :             return true;
     116             :         }
     117          27 :         return false;
     118             :     }
     119             : };
     120             : 
     121         135 : int GDALVectorConcatOutputDataset::GetLayerCount() const
     122             : {
     123         135 :     return static_cast<int>(m_layers.size());
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                     GDALVectorConcatRenamedLayer                     */
     128             : /************************************************************************/
     129             : 
     130             : class GDALVectorConcatRenamedLayer final : public OGRLayerDecorator
     131             : {
     132             :   public:
     133           7 :     GDALVectorConcatRenamedLayer(OGRLayer *poSrcLayer,
     134             :                                  const std::string &newName)
     135           7 :         : OGRLayerDecorator(poSrcLayer, false), m_newName(newName)
     136             :     {
     137           7 :     }
     138             : 
     139             :     const char *GetName() const override;
     140             : 
     141             :   private:
     142             :     const std::string m_newName;
     143             : };
     144             : 
     145           7 : const char *GDALVectorConcatRenamedLayer::GetName() const
     146             : {
     147           7 :     return m_newName.c_str();
     148             : }
     149             : 
     150             : /************************************************************************/
     151             : /*                         BuildLayerName()                             */
     152             : /************************************************************************/
     153             : 
     154          16 : static std::string BuildLayerName(const std::string &layerNameTemplate,
     155             :                                   int dsIdx, const std::string &dsName,
     156             :                                   int lyrIdx, const std::string &lyrName)
     157             : {
     158          32 :     CPLString ret = layerNameTemplate;
     159          32 :     std::string baseName;
     160             :     VSIStatBufL sStat;
     161          16 :     if (VSIStatL(dsName.c_str(), &sStat) == 0)
     162           1 :         baseName = CPLGetBasenameSafe(dsName.c_str());
     163             : 
     164          16 :     if (baseName == lyrName)
     165             :     {
     166           1 :         ret = ret.replaceAll("{AUTO_NAME}", baseName);
     167             :     }
     168             :     else
     169             :     {
     170             :         ret = ret.replaceAll("{AUTO_NAME}",
     171          30 :                              std::string(baseName.empty() ? dsName : baseName)
     172          15 :                                  .append("_")
     173          15 :                                  .append(lyrName));
     174             :     }
     175             : 
     176             :     ret =
     177          16 :         ret.replaceAll("{DS_BASENAME}", !baseName.empty() ? baseName : dsName);
     178          16 :     ret = ret.replaceAll("{DS_NAME}", dsName);
     179          16 :     ret = ret.replaceAll("{DS_INDEX}", CPLSPrintf("%d", dsIdx));
     180          16 :     ret = ret.replaceAll("{LAYER_NAME}", lyrName);
     181          16 :     ret = ret.replaceAll("{LAYER_INDEX}", CPLSPrintf("%d", lyrIdx));
     182             : 
     183          32 :     return std::string(std::move(ret));
     184             : }
     185             : 
     186             : namespace
     187             : {
     188             : 
     189             : /************************************************************************/
     190             : /*                            OpenProxiedLayer()                        */
     191             : /************************************************************************/
     192             : 
     193             : struct PooledInitData
     194             : {
     195             :     std::unique_ptr<GDALDataset> poDS{};
     196             :     std::string osDatasetName{};
     197             :     std::vector<std::string> *pInputFormats = nullptr;
     198             :     std::vector<std::string> *pOpenOptions = nullptr;
     199             :     int iLayer = 0;
     200             : };
     201             : 
     202           8 : static OGRLayer *OpenProxiedLayer(void *pUserData)
     203             : {
     204           8 :     PooledInitData *pData = static_cast<PooledInitData *>(pUserData);
     205           8 :     pData->poDS.reset(GDALDataset::Open(
     206             :         pData->osDatasetName.c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
     207          16 :         pData->pInputFormats ? CPLStringList(*(pData->pInputFormats)).List()
     208             :                              : nullptr,
     209          16 :         pData->pOpenOptions ? CPLStringList(*(pData->pOpenOptions)).List()
     210             :                             : nullptr,
     211             :         nullptr));
     212           8 :     if (!pData->poDS)
     213           0 :         return nullptr;
     214           8 :     return pData->poDS->GetLayer(pData->iLayer);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                         ReleaseProxiedLayer()                        */
     219             : /************************************************************************/
     220             : 
     221           8 : static void ReleaseProxiedLayer(OGRLayer *, void *pUserData)
     222             : {
     223           8 :     PooledInitData *pData = static_cast<PooledInitData *>(pUserData);
     224           8 :     pData->poDS.reset();
     225           8 : }
     226             : 
     227             : /************************************************************************/
     228             : /*                        FreeProxiedLayerUserData()                    */
     229             : /************************************************************************/
     230             : 
     231           2 : static void FreeProxiedLayerUserData(void *pUserData)
     232             : {
     233           2 :     delete static_cast<PooledInitData *>(pUserData);
     234           2 : }
     235             : 
     236             : }  // namespace
     237             : 
     238             : /************************************************************************/
     239             : /*                   GDALVectorConcatAlgorithm::RunStep()               */
     240             : /************************************************************************/
     241             : 
     242          28 : bool GDALVectorConcatAlgorithm::RunStep(GDALPipelineStepRunContext &)
     243             : {
     244          28 :     std::unique_ptr<OGRSpatialReference> poSrcCRS;
     245          28 :     if (!m_srsCrs.empty())
     246             :     {
     247           1 :         poSrcCRS = std::make_unique<OGRSpatialReference>();
     248           1 :         poSrcCRS->SetFromUserInput(m_srsCrs.c_str());
     249           1 :         poSrcCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     250             :     }
     251             : 
     252          56 :     OGRSpatialReference oDstCRS;
     253          28 :     if (!m_dstCrs.empty())
     254             :     {
     255           4 :         oDstCRS.SetFromUserInput(m_dstCrs.c_str());
     256           4 :         oDstCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     257             :     }
     258             : 
     259             :     struct LayerDesc
     260             :     {
     261             :         int iDS = 0;
     262             :         int iLayer = 0;
     263             :         std::string osDatasetName{};
     264             :     };
     265             : 
     266          28 :     if (m_layerNameTemplate.empty())
     267             :     {
     268          25 :         if (m_mode == "single")
     269           1 :             m_layerNameTemplate = "merged";
     270          24 :         else if (m_mode == "stack")
     271           2 :             m_layerNameTemplate = "{AUTO_NAME}";
     272             :     }
     273           3 :     else if (m_mode == "merge-per-layer-name")
     274             :     {
     275           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
     276             :                     "'layer-name' name argument cannot be specified in "
     277             :                     "mode=merge-per-layer-name");
     278           1 :         return false;
     279             :     }
     280             : 
     281          27 :     if (m_sourceLayerFieldContent.empty())
     282          21 :         m_sourceLayerFieldContent = "{AUTO_NAME}";
     283           6 :     else if (m_sourceLayerFieldName.empty())
     284           1 :         m_sourceLayerFieldName = "source_ds_lyr";
     285             : 
     286             :     const int nMaxSimultaneouslyOpened =
     287          81 :         std::max(atoi(CPLGetConfigOption(
     288             :                      "GDAL_VECTOR_CONCAT_MAX_OPENED_DATASETS", "100")),
     289          27 :                  1);
     290             : 
     291             :     // First pass on input layers
     292          54 :     std::map<std::string, std::vector<LayerDesc>> allLayerNames;
     293          27 :     int iDS = 0;
     294          27 :     int nonOpenedDSCount = 0;
     295          68 :     for (auto &srcDS : m_inputDataset)
     296             :     {
     297          42 :         GDALDataset *poSrcDS = srcDS.GetDatasetRef();
     298           0 :         std::unique_ptr<GDALDataset> poTmpDS;
     299          42 :         if (!poSrcDS)
     300             :         {
     301          11 :             poTmpDS.reset(GDALDataset::Open(
     302          11 :                 srcDS.GetName().c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
     303          22 :                 CPLStringList(m_inputFormats).List(),
     304          22 :                 CPLStringList(m_openOptions).List(), nullptr));
     305          11 :             poSrcDS = poTmpDS.get();
     306          11 :             if (!poSrcDS)
     307           0 :                 return false;
     308          11 :             if (static_cast<int>(m_inputDataset.size()) <=
     309          11 :                 nMaxSimultaneouslyOpened)
     310             :             {
     311           9 :                 srcDS.Set(std::move(poTmpDS));
     312           9 :                 poSrcDS = srcDS.GetDatasetRef();
     313             :             }
     314             :             else
     315             :             {
     316           2 :                 ++nonOpenedDSCount;
     317             :             }
     318             :         }
     319             : 
     320          42 :         int iLayer = 0;
     321          91 :         for (const auto &poLayer : poSrcDS->GetLayers())
     322             :         {
     323          52 :             if (m_inputLayerNames.empty() ||
     324           0 :                 std::find(m_inputLayerNames.begin(), m_inputLayerNames.end(),
     325          52 :                           poLayer->GetName()) != m_inputLayerNames.end())
     326             :             {
     327          54 :                 if (!m_dstCrs.empty() && m_srsCrs.empty() &&
     328           5 :                     poLayer->GetSpatialRef() == nullptr)
     329             :                 {
     330           1 :                     ReportError(
     331             :                         CE_Failure, CPLE_AppDefined,
     332             :                         "Layer '%s' of '%s' has no spatial reference system",
     333           1 :                         poLayer->GetName(), poSrcDS->GetDescription());
     334           1 :                     return false;
     335             :                 }
     336          96 :                 LayerDesc layerDesc;
     337          48 :                 layerDesc.iDS = iDS;
     338          48 :                 layerDesc.iLayer = iLayer;
     339          48 :                 layerDesc.osDatasetName = poSrcDS->GetDescription();
     340             :                 const std::string outLayerName =
     341          48 :                     m_mode == "single" ? m_layerNameTemplate
     342          42 :                     : m_mode == "merge-per-layer-name"
     343          33 :                         ? std::string(poLayer->GetName())
     344           9 :                         : BuildLayerName(m_layerNameTemplate, iDS,
     345           9 :                                          poSrcDS->GetDescription(), iLayer,
     346         174 :                                          poLayer->GetName());
     347          48 :                 CPLDebugOnly("gdal_vector_concat", "%s,%s->%s",
     348             :                              poSrcDS->GetDescription(), poLayer->GetName(),
     349             :                              outLayerName.c_str());
     350          48 :                 allLayerNames[outLayerName].push_back(std::move(layerDesc));
     351             :             }
     352          49 :             ++iLayer;
     353             :         }
     354          41 :         ++iDS;
     355             :     }
     356             : 
     357          52 :     auto poUnionDS = std::make_unique<GDALVectorConcatOutputDataset>();
     358             : 
     359          26 :     if (nonOpenedDSCount > nMaxSimultaneouslyOpened)
     360             :         m_poLayerPool =
     361           1 :             std::make_unique<OGRLayerPool>(nMaxSimultaneouslyOpened);
     362             : 
     363          26 :     bool ret = true;
     364          59 :     for (const auto &[outLayerName, listOfLayers] : allLayerNames)
     365             :     {
     366          33 :         const int nLayerCount = static_cast<int>(listOfLayers.size());
     367             :         std::unique_ptr<OGRLayer *, VSIFreeReleaser> papoSrcLayers(
     368             :             static_cast<OGRLayer **>(
     369          33 :                 CPLCalloc(nLayerCount, sizeof(OGRLayer *))));
     370          81 :         for (int i = 0; i < nLayerCount; ++i)
     371             :         {
     372          48 :             auto &srcDS = m_inputDataset[listOfLayers[i].iDS];
     373          48 :             GDALDataset *poSrcDS = srcDS.GetDatasetRef();
     374           0 :             std::unique_ptr<GDALDataset> poTmpDS;
     375          48 :             if (!poSrcDS)
     376             :             {
     377           2 :                 poTmpDS.reset(GDALDataset::Open(
     378           2 :                     listOfLayers[i].osDatasetName.c_str(),
     379             :                     GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
     380           4 :                     CPLStringList(m_inputFormats).List(),
     381           4 :                     CPLStringList(m_openOptions).List(), nullptr));
     382           2 :                 poSrcDS = poTmpDS.get();
     383           2 :                 if (!poSrcDS)
     384           0 :                     return false;
     385             :             }
     386          48 :             OGRLayer *poSrcLayer = poSrcDS->GetLayer(listOfLayers[i].iLayer);
     387             : 
     388          48 :             if (m_poLayerPool)
     389             :             {
     390           4 :                 auto pData = std::make_unique<PooledInitData>();
     391           2 :                 pData->osDatasetName = listOfLayers[i].osDatasetName;
     392           2 :                 pData->pInputFormats = &m_inputFormats;
     393           2 :                 pData->pOpenOptions = &m_openOptions;
     394           2 :                 pData->iLayer = listOfLayers[i].iLayer;
     395             :                 auto proxiedLayer = std::make_unique<OGRProxiedLayer>(
     396           0 :                     m_poLayerPool.get(), OpenProxiedLayer, ReleaseProxiedLayer,
     397           2 :                     FreeProxiedLayerUserData, pData.release());
     398           2 :                 proxiedLayer->SetDescription(poSrcLayer->GetDescription());
     399           2 :                 m_tempLayersKeeper.push_back(std::move(proxiedLayer));
     400           2 :                 poSrcLayer = m_tempLayersKeeper.back().get();
     401             :             }
     402          46 :             else if (poTmpDS)
     403             :             {
     404           0 :                 srcDS.Set(std::move(poTmpDS));
     405             :             }
     406             : 
     407          48 :             if (m_sourceLayerFieldName.empty())
     408             :             {
     409          41 :                 papoSrcLayers.get()[i] = poSrcLayer;
     410             :             }
     411             :             else
     412             :             {
     413             :                 const std::string newSrcLayerName = BuildLayerName(
     414           7 :                     m_sourceLayerFieldContent, listOfLayers[i].iDS,
     415           7 :                     listOfLayers[i].osDatasetName.c_str(),
     416          35 :                     listOfLayers[i].iLayer, poSrcLayer->GetName());
     417           7 :                 ret = !newSrcLayerName.empty() && ret;
     418             :                 auto poTmpLayer =
     419             :                     std::make_unique<GDALVectorConcatRenamedLayer>(
     420           7 :                         poSrcLayer, newSrcLayerName);
     421           7 :                 m_tempLayersKeeper.push_back(std::move(poTmpLayer));
     422           7 :                 papoSrcLayers.get()[i] = m_tempLayersKeeper.back().get();
     423             :             }
     424             :         }
     425             : 
     426             :         // Auto-wrap source layers if needed
     427          33 :         if (!m_dstCrs.empty())
     428             :         {
     429           8 :             for (int i = 0; i < nLayerCount; ++i)
     430             :             {
     431             :                 const OGRSpatialReference *poSrcLayerCRS;
     432           5 :                 if (poSrcCRS)
     433           1 :                     poSrcLayerCRS = poSrcCRS.get();
     434             :                 else
     435           4 :                     poSrcLayerCRS = papoSrcLayers.get()[i]->GetSpatialRef();
     436           5 :                 if (!poSrcLayerCRS->IsSame(&oDstCRS))
     437             :                 {
     438             :                     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     439             :                         OGRCreateCoordinateTransformation(poSrcLayerCRS,
     440           6 :                                                           &oDstCRS));
     441             :                     auto poReversedCT =
     442             :                         std::unique_ptr<OGRCoordinateTransformation>(
     443             :                             OGRCreateCoordinateTransformation(&oDstCRS,
     444           6 :                                                               poSrcLayerCRS));
     445           3 :                     ret = (poCT != nullptr) && (poReversedCT != nullptr);
     446           3 :                     if (ret)
     447             :                     {
     448           3 :                         m_tempLayersKeeper.push_back(
     449           3 :                             std::make_unique<OGRWarpedLayer>(
     450           3 :                                 papoSrcLayers.get()[i], /* iGeomField = */ 0,
     451           3 :                                 /*bTakeOwnership = */ false, poCT.release(),
     452           3 :                                 poReversedCT.release()));
     453           3 :                         papoSrcLayers.get()[i] =
     454           3 :                             m_tempLayersKeeper.back().get();
     455             :                     }
     456             :                 }
     457             :             }
     458             :         }
     459             : 
     460             :         auto poUnionLayer = std::make_unique<OGRUnionLayer>(
     461          33 :             outLayerName.c_str(), nLayerCount, papoSrcLayers.release(),
     462          66 :             /* bTakeLayerOwnership = */ false);
     463             : 
     464          33 :         if (!m_sourceLayerFieldName.empty())
     465             :         {
     466           7 :             poUnionLayer->SetSourceLayerFieldName(
     467             :                 m_sourceLayerFieldName.c_str());
     468             :         }
     469             : 
     470             :         const FieldUnionStrategy eStrategy =
     471          33 :             m_fieldStrategy == "union" ? FIELD_UNION_ALL_LAYERS
     472          33 :                                        : FIELD_INTERSECTION_ALL_LAYERS;
     473          33 :         poUnionLayer->SetFields(eStrategy, 0, nullptr, 0, nullptr);
     474             : 
     475          33 :         poUnionDS->AddLayer(std::move(poUnionLayer));
     476             :     }
     477             : 
     478          26 :     if (ret)
     479             :     {
     480          26 :         m_outputDataset.Set(std::move(poUnionDS));
     481             :     }
     482          26 :     return ret;
     483             : }
     484             : 
     485             : /************************************************************************/
     486             : /*                GDALVectorConcatAlgorithm::RunImpl()                  */
     487             : /************************************************************************/
     488             : 
     489          54 : bool GDALVectorConcatAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     490             :                                         void *pProgressData)
     491             : {
     492          54 :     if (m_standaloneStep)
     493             :     {
     494          27 :         GDALVectorWriteAlgorithm writeAlg;
     495         459 :         for (auto &arg : writeAlg.GetArgs())
     496             :         {
     497         432 :             if (arg->GetName() != GDAL_ARG_NAME_OUTPUT_LAYER)
     498             :             {
     499         405 :                 auto stepArg = GetArg(arg->GetName());
     500         405 :                 if (stepArg && stepArg->IsExplicitlySet())
     501             :                 {
     502          54 :                     arg->SetSkipIfAlreadySet(true);
     503          54 :                     arg->SetFrom(*stepArg);
     504             :                 }
     505             :             }
     506             :         }
     507             : 
     508             :         // Already checked by GDALAlgorithm::Run()
     509          27 :         CPLAssert(!m_executionForStreamOutput ||
     510             :                   EQUAL(m_format.c_str(), "stream"));
     511             : 
     512          27 :         m_standaloneStep = false;
     513          27 :         bool ret = Run(pfnProgress, pProgressData);
     514          27 :         m_standaloneStep = true;
     515          27 :         if (ret)
     516             :         {
     517          25 :             if (m_format == "stream")
     518             :             {
     519           2 :                 ret = true;
     520             :             }
     521             :             else
     522             :             {
     523          23 :                 writeAlg.m_inputDataset.clear();
     524          23 :                 writeAlg.m_inputDataset.resize(1);
     525          23 :                 writeAlg.m_inputDataset[0].Set(m_outputDataset.GetDatasetRef());
     526          23 :                 if (writeAlg.Run(pfnProgress, pProgressData))
     527             :                 {
     528          23 :                     m_outputDataset.Set(
     529             :                         writeAlg.m_outputDataset.GetDatasetRef());
     530          23 :                     ret = true;
     531             :                 }
     532             :             }
     533             :         }
     534             : 
     535          27 :         return ret;
     536             :     }
     537             :     else
     538             :     {
     539          27 :         GDALPipelineStepRunContext stepCtxt;
     540          27 :         stepCtxt.m_pfnProgress = pfnProgress;
     541          27 :         stepCtxt.m_pProgressData = pProgressData;
     542          27 :         return RunStep(stepCtxt);
     543             :     }
     544             : }
     545             : 
     546             : GDALVectorConcatAlgorithmStandalone::~GDALVectorConcatAlgorithmStandalone() =
     547             :     default;
     548             : 
     549             : //! @endcond

Generated by: LCOV version 1.14