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

Generated by: LCOV version 1.14