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

Generated by: LCOV version 1.14