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

Generated by: LCOV version 1.14