LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_explode.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 223 235 94.9 %
Date: 2026-06-23 16:35:19 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "explode" step of "vector pipeline"
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_explode.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_string.h"
      17             : #include "gdal_priv.h"
      18             : #include "ogr_p.h"
      19             : #include "ogrsf_frmts.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <cinttypes>
      23             : #include <list>
      24             : #include <memory>
      25             : #include <numeric>
      26             : #include <vector>
      27             : 
      28             : //! @cond Doxygen_Suppress
      29             : 
      30             : #ifndef _
      31             : #define _(x) (x)
      32             : #endif
      33             : 
      34             : /************************************************************************/
      35             : /*       GDALVectorExplodeAlgorithm::GDALVectorExplodeAlgorithm()       */
      36             : /************************************************************************/
      37             : 
      38         104 : GDALVectorExplodeAlgorithm::GDALVectorExplodeAlgorithm(bool standaloneStep)
      39             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      40         104 :                                       standaloneStep)
      41             : {
      42         104 :     AddActiveLayerArg(&m_activeLayer);
      43             : 
      44             :     {
      45             :         auto &arg =
      46         208 :             AddArg("field", 0, _("Attribute fields(s) to explode"), &m_fields)
      47         104 :                 .SetMetaVar("FIELD");
      48             : 
      49         312 :         SetAutoCompleteFunctionForFieldName(
      50         104 :             arg, nullptr, true, false, m_inputDataset, {"ALL"},
      51           2 :             [](const OGRFieldDefn *defn)
      52         106 :             { return OGR_GetFieldTypeIsList(defn->GetType()); });
      53             :     }
      54             : 
      55         104 :     AddArg("geometry", 0, _("Explode default geometry field"), &m_defaultGeom);
      56             : 
      57             :     {
      58             :         auto &arg = AddArg("geometry-field", 0,
      59         208 :                            _("Geometry field(s) to explode"), &m_geomFields)
      60         104 :                         .SetMetaVar("GEOMETRY-FIELD");
      61         312 :         SetAutoCompleteFunctionForFieldName(arg, nullptr, false, true,
      62         208 :                                             m_inputDataset, {"ALL"});
      63             :     }
      64             : 
      65             :     AddArg("index-field", 0, _("Name of the output index field"),
      66         208 :            &m_indexFieldName)
      67         104 :         .SetDefault(m_indexFieldName);
      68         104 : }
      69             : 
      70             : GDALVectorExplodeAlgorithmStandalone::~GDALVectorExplodeAlgorithmStandalone() =
      71             :     default;
      72             : 
      73             : namespace
      74             : {
      75             : 
      76             : class GDALVectorExplodeLayer final : public GDALVectorPipelineOutputLayer
      77             : {
      78             :   public:
      79          22 :     GDALVectorExplodeLayer(OGRLayer &srcLayer,
      80             :                            const std::vector<std::string> &fieldsToExplode,
      81             :                            const std::vector<std::string> &geomFieldsToExplode,
      82             :                            const std::string &indexFieldName)
      83          22 :         : GDALVectorPipelineOutputLayer(srcLayer),
      84             :           m_fieldsToExplode(fieldsToExplode),
      85             :           m_geomFieldsToExplode(geomFieldsToExplode),
      86          22 :           m_indexFieldName(indexFieldName)
      87             :     {
      88          22 :         if (!PrepareFeatureDefn())
      89             :         {
      90           1 :             m_setupError = true;
      91             :         }
      92          22 :     }
      93             : 
      94          22 :     bool PrepareFeatureDefn()
      95             :     {
      96          22 :         m_poFeatureDefn.reset(
      97          22 :             OGRFeatureDefn::CreateFeatureDefn(m_srcLayer.GetName()));
      98             : 
      99             :         // Avoid creating geometry field with null SRS
     100             :         // We'll copy it in later from the source layer
     101          22 :         m_poFeatureDefn->DeleteGeomFieldDefn(0);
     102             : 
     103          22 :         const bool addIndexField = !m_indexFieldName.empty();
     104             : 
     105          22 :         if (addIndexField)
     106             :         {
     107             :             auto poIdxField = std::make_unique<OGRFieldDefn>(
     108           2 :                 m_indexFieldName.c_str(), OFTInteger);
     109           1 :             m_poFeatureDefn->AddFieldDefn(std::move(poIdxField));
     110             :         }
     111             : 
     112          22 :         const OGRFeatureDefn *poSrcDefn = m_srcLayer.GetLayerDefn();
     113             : 
     114             :         // By default, all fields copied as-is.
     115          22 :         m_unnestedFieldSrcToDstMap.resize(poSrcDefn->GetFieldCount(), -1);
     116          22 :         m_passThroughFieldSrcToDstMap.resize(poSrcDefn->GetFieldCount());
     117          22 :         std::iota(m_passThroughFieldSrcToDstMap.begin(),
     118             :                   m_passThroughFieldSrcToDstMap.end(), addIndexField ? 1 : 0);
     119             : 
     120          22 :         m_geomFieldExploded.resize(poSrcDefn->GetGeomFieldCount(), false);
     121             : 
     122          42 :         for (const auto &fieldName : m_fieldsToExplode)
     123             :         {
     124          21 :             const int iSrcField = poSrcDefn->GetFieldIndex(fieldName.c_str());
     125          21 :             if (iSrcField < 0)
     126             :             {
     127           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     128             :                          "Field '%s' not found in source layer.",
     129             :                          fieldName.c_str());
     130           1 :                 return false;
     131             :             }
     132             : 
     133             :             const OGRFieldDefn *poSrcFieldDefn =
     134          20 :                 poSrcDefn->GetFieldDefn(iSrcField);
     135          20 :             const auto eSrcType = poSrcFieldDefn->GetType();
     136          20 :             if (OGR_GetFieldTypeIsList(eSrcType))
     137             :             {
     138          17 :                 m_passThroughFieldSrcToDstMap[iSrcField] = -1;
     139          17 :                 m_unnestedFieldSrcToDstMap[iSrcField] =
     140          17 :                     iSrcField + addIndexField;
     141             :             }
     142             :         }
     143             : 
     144          39 :         for (const auto &fieldName : m_geomFieldsToExplode)
     145             :         {
     146             :             // Is it a geometry field?
     147          18 :             int iSrcGeomField = poSrcDefn->GetGeomFieldIndex(fieldName.c_str());
     148             : 
     149             :             // Interpret --geometry-field _OGR_GEOMETRY_ as the first geometry
     150             :             // field, regardless of what it is actually named
     151          18 :             if (iSrcGeomField < 0)
     152             :             {
     153          24 :                 if (poSrcDefn->GetGeomFieldCount() > 0 &&
     154          12 :                     EQUAL(fieldName.c_str(),
     155             :                           OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME))
     156             :                 {
     157          10 :                     iSrcGeomField = 0;
     158             :                 }
     159             :             }
     160             : 
     161             :             // Didn't find anything by name. Check by index.
     162          20 :             if (iSrcGeomField < 0 &&
     163           2 :                 std::all_of(
     164           2 :                     fieldName.begin(), fieldName.end(), [](char c)
     165           2 :                     { return std::isdigit(static_cast<unsigned char>(c)); }))
     166             :             {
     167           2 :                 const int iGeomField = std::atoi(fieldName.c_str());
     168             : 
     169           2 :                 if (iGeomField < poSrcDefn->GetGeomFieldCount())
     170             :                 {
     171           2 :                     iSrcGeomField = iGeomField;
     172             :                 }
     173             :             }
     174             : 
     175          18 :             if (iSrcGeomField < 0)
     176             :             {
     177           0 :                 CPLError(
     178             :                     CE_Failure, CPLE_AppDefined,
     179             :                     "Could not find geometry field '%s' in source layer '%s'",
     180           0 :                     fieldName.c_str(), m_srcLayer.GetName());
     181           0 :                 return false;
     182             :             }
     183             : 
     184          18 :             m_geomFieldExploded[iSrcGeomField] = true;
     185             :         }
     186             : 
     187             :         // Create attribute fields
     188          59 :         for (int iSrcField = 0; iSrcField < poSrcDefn->GetFieldCount();
     189             :              iSrcField++)
     190             :         {
     191          38 :             const auto *poSrcFieldDefn = poSrcDefn->GetFieldDefn(iSrcField);
     192          38 :             std::unique_ptr<OGRFieldDefn> poDstFieldDefn;
     193             : 
     194          38 :             if (m_passThroughFieldSrcToDstMap[iSrcField] != -1)
     195             :             {
     196             :                 poDstFieldDefn =
     197          21 :                     std::make_unique<OGRFieldDefn>(*poSrcFieldDefn);
     198             :             }
     199             :             else
     200             :             {
     201             :                 const auto eScalarType =
     202          17 :                     OGR_GetFieldTypeAsScalar(poSrcFieldDefn->GetType());
     203          17 :                 poDstFieldDefn = std::make_unique<OGRFieldDefn>(
     204          34 :                     poSrcFieldDefn->GetNameRef(), eScalarType);
     205             :             }
     206             : 
     207          38 :             m_poFeatureDefn->AddFieldDefn(std::move(poDstFieldDefn));
     208             :         }
     209             : 
     210             :         // Create geometry fields
     211          44 :         for (int iSrcGeomField = 0;
     212          44 :              iSrcGeomField < poSrcDefn->GetGeomFieldCount(); iSrcGeomField++)
     213             :         {
     214             :             const OGRGeomFieldDefn *poSrcGeomFieldDefn =
     215          23 :                 poSrcDefn->GetGeomFieldDefn(iSrcGeomField);
     216          23 :             std::unique_ptr<OGRGeomFieldDefn> poDstGeomFieldDefn;
     217             : 
     218          23 :             if (m_geomFieldExploded[iSrcGeomField])
     219             :             {
     220             :                 const auto eDstType =
     221          18 :                     OGR_GT_GetSingle(poSrcGeomFieldDefn->GetType());
     222          18 :                 poDstGeomFieldDefn = std::make_unique<OGRGeomFieldDefn>(
     223          18 :                     poSrcGeomFieldDefn->GetNameRef(), eDstType);
     224          36 :                 poDstGeomFieldDefn->SetSpatialRef(
     225          18 :                     poSrcGeomFieldDefn->GetSpatialRef());
     226             :             }
     227             :             else
     228             :             {
     229             :                 poDstGeomFieldDefn =
     230           5 :                     std::make_unique<OGRGeomFieldDefn>(*poSrcGeomFieldDefn);
     231             :             }
     232             : 
     233          23 :             m_poFeatureDefn->AddGeomFieldDefn(std::move(poDstGeomFieldDefn));
     234             :         }
     235             : 
     236          21 :         return true;
     237             :     }
     238             : 
     239           3 :     const char *GetDescription() const override
     240             :     {
     241           3 :         return m_poFeatureDefn->GetName();
     242             :     }
     243             : 
     244         368 :     const OGRFeatureDefn *GetLayerDefn() const override
     245             :     {
     246         368 :         return m_poFeatureDefn.get();
     247             :     }
     248             : 
     249         133 :     void ResetReading() override
     250             :     {
     251         133 :         m_nextFID = 1;
     252         133 :         GDALVectorPipelineOutputLayer::ResetReading();
     253         133 :     }
     254             : 
     255          54 :     int TestCapability(const char *pszCap) const override
     256             :     {
     257          54 :         if (EQUAL(pszCap, OLCFastGetExtent) ||
     258          52 :             EQUAL(pszCap, OLCFastGetExtent3D) ||
     259          52 :             EQUAL(pszCap, OLCStringsAsUTF8) ||
     260          38 :             EQUAL(pszCap, OLCCurveGeometries) ||
     261          34 :             EQUAL(pszCap, OLCMeasuredGeometries) ||
     262          30 :             EQUAL(pszCap, OLCZGeometries))
     263             :         {
     264          27 :             return m_srcLayer.TestCapability(pszCap);
     265             :         }
     266             : 
     267          27 :         return false;
     268             :     }
     269             : 
     270         162 :     bool TranslateFeature(
     271             :         std::unique_ptr<OGRFeature> poSrcFeature,
     272             :         std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
     273             :     {
     274         162 :         if (m_setupError)
     275             :         {
     276           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     277             :                      "Failed to prepare output layer.");
     278           1 :             return false;
     279             :         }
     280             : 
     281         161 :         int nDstFeatures = 1;
     282             : 
     283         627 :         for (int iDstFeature = 0; iDstFeature < nDstFeatures; iDstFeature++)
     284             :         {
     285             :             auto poDstFeature =
     286         472 :                 std::make_unique<OGRFeature>(m_poFeatureDefn.get());
     287         472 :             if (!m_indexFieldName.empty())
     288             :             {
     289           9 :                 poDstFeature->SetField(0, iDstFeature);
     290             :             }
     291             : 
     292         944 :             if (poDstFeature->SetFieldsFrom(
     293         472 :                     poSrcFeature.get(), m_passThroughFieldSrcToDstMap.data(),
     294         472 :                     true) != OGRERR_NONE)
     295             :             {
     296           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     297             :                          "Failed to set fields of output feature");
     298           0 :                 return false;
     299             :             }
     300             : 
     301        2576 :             for (int iSrcArrayField = 0;
     302        2576 :                  iSrcArrayField <
     303        2576 :                  static_cast<int>(m_unnestedFieldSrcToDstMap.size());
     304             :                  iSrcArrayField++)
     305             :             {
     306             :                 const int iDstField =
     307        2108 :                     m_unnestedFieldSrcToDstMap[iSrcArrayField];
     308        2108 :                 if (iDstField < 0)
     309             :                 {
     310        1622 :                     continue;
     311             :                 }
     312             : 
     313             :                 const auto poSrcFieldDefn =
     314         486 :                     poSrcFeature->GetFieldDefnRef(iSrcArrayField);
     315         486 :                 const auto eSrcType = poSrcFieldDefn->GetType();
     316         486 :                 int nArrayLength = -1;
     317         486 :                 if (eSrcType == OFTIntegerList)
     318             :                 {
     319         423 :                     const int *pnArray = poSrcFeature->GetFieldAsIntegerList(
     320             :                         iSrcArrayField, &nArrayLength);
     321         423 :                     if (iDstFeature >= nArrayLength)
     322             :                     {
     323           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     324             :                                  "Field '%s' of source feature %" PRId64
     325             :                                  " does not have enough elements.",
     326             :                                  poSrcFieldDefn->GetNameRef(),
     327           1 :                                  static_cast<int64_t>(poSrcFeature->GetFID()));
     328           4 :                         return false;
     329             :                     }
     330         422 :                     poDstFeature->SetField(iDstField, pnArray[iDstFeature]);
     331             :                 }
     332          63 :                 else if (eSrcType == OFTInteger64List)
     333             :                 {
     334             :                     const GIntBig *pnArray =
     335          21 :                         poSrcFeature->GetFieldAsInteger64List(iSrcArrayField,
     336             :                                                               &nArrayLength);
     337          21 :                     if (iDstFeature >= nArrayLength)
     338             :                     {
     339           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     340             :                                  "Field '%s' of source feature %" PRId64
     341             :                                  " does not have enough elements.",
     342             :                                  poSrcFieldDefn->GetNameRef(),
     343           1 :                                  static_cast<int64_t>(poSrcFeature->GetFID()));
     344           1 :                         return false;
     345             :                     }
     346          20 :                     poDstFeature->SetField(iDstField, pnArray[iDstFeature]);
     347             :                 }
     348          42 :                 else if (eSrcType == OFTRealList)
     349             :                 {
     350             :                     const double *padfArray =
     351          21 :                         poSrcFeature->GetFieldAsDoubleList(iSrcArrayField,
     352             :                                                            &nArrayLength);
     353          21 :                     if (iDstFeature >= nArrayLength)
     354             :                     {
     355           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     356             :                                  "Field '%s' of source feature %" PRId64
     357             :                                  " does not have enough elements.",
     358             :                                  poSrcFieldDefn->GetNameRef(),
     359           1 :                                  static_cast<int64_t>(poSrcFeature->GetFID()));
     360           1 :                         return false;
     361             :                     }
     362          20 :                     poDstFeature->SetField(iDstField, padfArray[iDstFeature]);
     363             :                 }
     364          21 :                 else if (eSrcType == OFTStringList)
     365             :                 {
     366             :                     CSLConstList papszArray =
     367          21 :                         poSrcFeature->GetFieldAsStringList(iSrcArrayField);
     368          21 :                     nArrayLength = CSLCount(papszArray);
     369          21 :                     if (iDstFeature >= nArrayLength)
     370             :                     {
     371           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     372             :                                  "Field '%s' of source feature %" PRId64
     373             :                                  " does not have enough elements.",
     374             :                                  poSrcFieldDefn->GetNameRef(),
     375           1 :                                  static_cast<int64_t>(poSrcFeature->GetFID()));
     376           1 :                         return false;
     377             :                     }
     378          20 :                     poDstFeature->SetField(iDstField, papszArray[iDstFeature]);
     379             :                 }
     380         482 :                 nDstFeatures = std::max(nDstFeatures, nArrayLength);
     381             :             }
     382             : 
     383         935 :             for (int iGeomField = 0;
     384         935 :                  iGeomField < poSrcFeature->GetGeomFieldCount(); iGeomField++)
     385             :             {
     386         469 :                 if (m_geomFieldExploded[iGeomField])
     387             :                 {
     388           0 :                     std::unique_ptr<OGRGeometry> poDstGeom;
     389             : 
     390             :                     OGRGeometry *poSrcGeom(
     391          55 :                         poSrcFeature->GetGeomFieldRef(iGeomField));
     392             : 
     393             :                     const bool bSrcIsCollection =
     394         103 :                         poSrcGeom != nullptr &&
     395          48 :                         OGR_GT_IsSubClassOf(
     396          48 :                             wkbFlatten(poSrcGeom->getGeometryType()),
     397          55 :                             wkbGeometryCollection);
     398             : 
     399          55 :                     if (bSrcIsCollection)
     400             :                     {
     401             :                         OGRGeometryCollection *poColl =
     402          44 :                             poSrcGeom->toGeometryCollection();
     403             : 
     404          44 :                         auto nGeoms = poColl->getNumGeometries();
     405          44 :                         nDstFeatures = std::max(nDstFeatures, nGeoms);
     406             : 
     407          44 :                         if (nGeoms == 0)
     408             :                         {
     409           1 :                             CPLError(
     410             :                                 CE_Failure, CPLE_AppDefined,
     411             :                                 "Geometry field '%s' of source feature %" PRId64
     412             :                                 " has %d elements (expected %d)",
     413           1 :                                 poSrcFeature->GetDefnRef()
     414           1 :                                     ->GetGeomFieldDefn(iGeomField)
     415             :                                     ->GetNameRef(),
     416           1 :                                 static_cast<int64_t>(poSrcFeature->GetFID()),
     417             :                                 nGeoms + iDstFeature, nDstFeatures);
     418           1 :                             return false;
     419             :                         }
     420             : 
     421          43 :                         poDstGeom = poColl->stealGeometry(0);
     422             :                     }
     423             :                     else
     424             :                     {
     425          13 :                         if (iDstFeature > 1 &&
     426           2 :                             apoOutFeatures.front()->GetGeomFieldRef(
     427             :                                 iGeomField) != nullptr)
     428             :                         {
     429           1 :                             CPLError(
     430             :                                 CE_Failure, CPLE_AppDefined,
     431             :                                 "Geometry field '%s' of source feature %" PRId64
     432             :                                 " is not a collection.",
     433           1 :                                 poSrcFeature->GetDefnRef()
     434           1 :                                     ->GetGeomFieldDefn(iGeomField)
     435             :                                     ->GetNameRef(),
     436           1 :                                 static_cast<int64_t>(poSrcFeature->GetFID()));
     437           1 :                             return false;
     438             :                         }
     439             : 
     440          10 :                         poDstGeom.reset(
     441             :                             poSrcFeature->StealGeometry(iGeomField));
     442             :                     }
     443             : 
     444         106 :                     poDstFeature->SetGeomField(iGeomField,
     445          53 :                                                std::move(poDstGeom));
     446             :                 }
     447             :                 else
     448             :                 {
     449           0 :                     std::unique_ptr<OGRGeometry> poSrcGeom;
     450             : 
     451         414 :                     if (apoOutFeatures.empty())
     452             :                     {
     453         213 :                         poSrcGeom.reset(
     454             :                             poSrcFeature->StealGeometry(iGeomField));
     455             :                     }
     456             :                     else
     457             :                     {
     458         402 :                         poSrcGeom.reset(apoOutFeatures.front()
     459         201 :                                             ->GetGeomFieldRef(iGeomField)
     460         201 :                                             ->clone());
     461             :                     }
     462             : 
     463         828 :                     poDstFeature->SetGeomField(iGeomField,
     464         414 :                                                std::move(poSrcGeom));
     465             :                 }
     466             :             }
     467             : 
     468         466 :             poDstFeature->SetFID(m_nextFID++);
     469         466 :             if (PassesFilters(poDstFeature.get()))
     470         354 :                 apoOutFeatures.push_back(std::move(poDstFeature));
     471             :         }
     472             : 
     473         155 :         return true;
     474             :     }
     475             : 
     476             :   protected:
     477           4 :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
     478             :                       bool bForce) override
     479             :     {
     480           4 :         return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
     481             :     }
     482             : 
     483           0 :     OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
     484             :                         bool bForce) override
     485             :     {
     486           0 :         return m_srcLayer.GetExtent3D(iGeomField, psExtent3D, bForce);
     487             :     }
     488             : 
     489             :   private:
     490             :     std::vector<int> m_passThroughFieldSrcToDstMap{};
     491             :     std::vector<int> m_unnestedFieldSrcToDstMap{};
     492             :     std::vector<bool> m_geomFieldExploded{};
     493             :     std::vector<std::string> m_fieldsToExplode{};
     494             :     std::vector<std::string> m_geomFieldsToExplode{};
     495             :     std::string m_indexFieldName{};
     496             :     bool m_setupError{false};
     497             :     OGRFeatureDefnRefCountedPtr m_poFeatureDefn{nullptr};
     498             :     GIntBig m_nextFID{1};
     499             : 
     500             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorExplodeLayer)
     501             : };
     502             : 
     503             : }  // namespace
     504             : 
     505             : /************************************************************************/
     506             : /*                GDALVectorExplodeAlgorithm::RunStep()                 */
     507             : /************************************************************************/
     508             : 
     509          21 : bool GDALVectorExplodeAlgorithm::RunStep(GDALPipelineStepRunContext &)
     510             : {
     511          21 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     512          21 :     CPLAssert(poSrcDS);
     513             : 
     514          42 :     auto poOutDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
     515             : 
     516          21 :     if (m_defaultGeom)
     517             :     {
     518           4 :         m_geomFields.emplace_back(OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME);
     519             :     }
     520             : 
     521          21 :     if (m_fields.empty() && m_geomFields.empty())
     522             :     {
     523           0 :         ReportError(CE_Failure, CPLE_IllegalArg,
     524             :                     "At least one field or geometry field must be specified");
     525           0 :         return false;
     526             :     }
     527             : 
     528          43 :     for (OGRLayer *poSrcLayer : poSrcDS->GetLayers())
     529             :     {
     530          22 :         if (!poSrcLayer)
     531           0 :             continue;
     532             : 
     533          24 :         if (!m_activeLayer.empty() &&
     534           2 :             poSrcLayer->GetDescription() != m_activeLayer)
     535             :         {
     536           2 :             poOutDS->AddLayer(
     537             :                 *poSrcLayer,
     538           2 :                 std::make_unique<GDALVectorPipelinePassthroughLayer>(
     539             :                     *poSrcLayer));
     540             :         }
     541             : 
     542          22 :         const auto *poLayerDefn = poSrcLayer->GetLayerDefn();
     543             : 
     544          44 :         auto fieldsForLayer = m_fields;
     545          44 :         auto geomFieldsForLayer = m_geomFields;
     546             : 
     547          22 :         if (geomFieldsForLayer.size() == 1 && geomFieldsForLayer[0] == "ALL")
     548             :         {
     549           2 :             geomFieldsForLayer.clear();
     550           2 :             for (int iGeomField = 0;
     551           4 :                  iGeomField < poLayerDefn->GetGeomFieldCount(); iGeomField++)
     552             :             {
     553             :                 geomFieldsForLayer.emplace_back(
     554           2 :                     poLayerDefn->GetGeomFieldDefn(iGeomField)->GetNameRef());
     555             :             }
     556             :         }
     557             : 
     558          22 :         if (fieldsForLayer.size() == 1 && fieldsForLayer[0] == "ALL")
     559             :         {
     560           2 :             fieldsForLayer.clear();
     561           4 :             for (int iField = 0; iField < poLayerDefn->GetFieldCount();
     562             :                  iField++)
     563             :             {
     564             :                 fieldsForLayer.emplace_back(
     565           2 :                     poLayerDefn->GetFieldDefn(iField)->GetNameRef());
     566             :             }
     567             :         }
     568             : 
     569             :         auto poOutLayer = std::make_unique<GDALVectorExplodeLayer>(
     570          22 :             *poSrcLayer, fieldsForLayer, geomFieldsForLayer, m_indexFieldName);
     571          22 :         poOutDS->AddLayer(*poSrcLayer, std::move(poOutLayer));
     572             :     }
     573             : 
     574          21 :     m_outputDataset.Set(std::move(poOutDS));
     575          21 :     return true;
     576             : }
     577             : 
     578             : //! @endcond

Generated by: LCOV version 1.14