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

Generated by: LCOV version 1.14