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

Generated by: LCOV version 1.14