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

Generated by: LCOV version 1.14