LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_explode_collections.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 119 120 99.2 %
Date: 2026-04-23 19:47:11 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "gdal vector explode-collections"
       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_explode_collections.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "ogrsf_frmts.h"
      17             : 
      18             : #include <list>
      19             : #include <utility>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*               GDALVectorExplodeCollectionsAlgorithm()                */
      29             : /************************************************************************/
      30             : 
      31          88 : GDALVectorExplodeCollectionsAlgorithm::GDALVectorExplodeCollectionsAlgorithm(
      32          88 :     bool standaloneStep)
      33             :     : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
      34          88 :                                       standaloneStep, m_opts)
      35             : {
      36         176 :     AddArg("geometry-type", 0, _("Geometry type"), &m_opts.m_type)
      37             :         .SetAutoCompleteFunction(
      38           2 :             [](const std::string &currentValue)
      39             :             {
      40           2 :                 std::vector<std::string> oRet;
      41          18 :                 for (const char *type :
      42             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
      43             :                       "CIRCULARSTRING", "COMPOUNDCURVE", "CURVEPOLYGON",
      44          20 :                       "POLYHEDRALSURFACE", "TIN"})
      45             :                 {
      46          27 :                     if (currentValue.empty() ||
      47           9 :                         STARTS_WITH(type, currentValue.c_str()))
      48             :                     {
      49          10 :                         oRet.push_back(type);
      50          10 :                         oRet.push_back(std::string(type).append("Z"));
      51          10 :                         oRet.push_back(std::string(type).append("M"));
      52          10 :                         oRet.push_back(std::string(type).append("ZM"));
      53             :                     }
      54             :                 }
      55           2 :                 return oRet;
      56          88 :             });
      57             : 
      58             :     AddArg("skip-on-type-mismatch", 0,
      59             :            _("Skip feature when change of feature geometry type failed"),
      60          88 :            &m_opts.m_skip);
      61          88 : }
      62             : 
      63             : namespace
      64             : {
      65             : 
      66             : /************************************************************************/
      67             : /*              GDALVectorExplodeCollectionsAlgorithmLayer              */
      68             : /************************************************************************/
      69             : 
      70             : class GDALVectorExplodeCollectionsAlgorithmLayer final
      71             :     : public GDALVectorPipelineOutputLayer
      72             : {
      73             :   private:
      74             :     const GDALVectorExplodeCollectionsAlgorithm::Options m_opts;
      75             :     int m_iGeomIdx = -1;
      76             :     const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
      77             :     GIntBig m_nextFID = 1;
      78             : 
      79             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorExplodeCollectionsAlgorithmLayer)
      80             : 
      81             :     void TranslateFeature(
      82             :         std::unique_ptr<OGRFeature> poSrcFeature,
      83             :         std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
      84             : 
      85          31 :     bool IsSelectedGeomField(int idx) const
      86             :     {
      87          31 :         return m_iGeomIdx < 0 || idx == m_iGeomIdx;
      88             :     }
      89             : 
      90             :   public:
      91             :     GDALVectorExplodeCollectionsAlgorithmLayer(
      92             :         OGRLayer &oSrcLayer,
      93             :         const GDALVectorExplodeCollectionsAlgorithm::Options &opts);
      94             : 
      95         221 :     const OGRFeatureDefn *GetLayerDefn() const override
      96             :     {
      97         221 :         return m_poFeatureDefn.get();
      98             :     }
      99             : 
     100         121 :     void ResetReading() override
     101             :     {
     102         121 :         m_nextFID = 1;
     103         121 :         GDALVectorPipelineOutputLayer::ResetReading();
     104         121 :     }
     105             : 
     106           5 :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
     107             :                       bool bForce) override
     108             :     {
     109           5 :         return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
     110             :     }
     111             : 
     112          39 :     int TestCapability(const char *pszCap) const override
     113             :     {
     114          39 :         if (EQUAL(pszCap, OLCCurveGeometries) ||
     115          35 :             EQUAL(pszCap, OLCMeasuredGeometries) ||
     116          31 :             EQUAL(pszCap, OLCZGeometries) || EQUAL(pszCap, OLCFastGetExtent) ||
     117          26 :             EQUAL(pszCap, OLCStringsAsUTF8))
     118             :         {
     119          27 :             return m_srcLayer.TestCapability(pszCap);
     120             :         }
     121          12 :         return false;
     122             :     }
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /*             GDALVectorExplodeCollectionsAlgorithmLayer()             */
     127             : /************************************************************************/
     128             : 
     129           8 : GDALVectorExplodeCollectionsAlgorithmLayer::
     130             :     GDALVectorExplodeCollectionsAlgorithmLayer(
     131             :         OGRLayer &oSrcLayer,
     132           8 :         const GDALVectorExplodeCollectionsAlgorithm::Options &opts)
     133             :     : GDALVectorPipelineOutputLayer(oSrcLayer), m_opts(opts),
     134           8 :       m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
     135             : {
     136           8 :     SetDescription(oSrcLayer.GetDescription());
     137           8 :     SetMetadata(oSrcLayer.GetMetadata());
     138             : 
     139           8 :     if (!m_opts.m_geomField.empty())
     140             :     {
     141           4 :         const int nIdx = oSrcLayer.GetLayerDefn()->GetGeomFieldIndex(
     142           2 :             m_opts.m_geomField.c_str());
     143           2 :         if (nIdx >= 0)
     144           2 :             m_iGeomIdx = nIdx;
     145             :         else
     146           0 :             m_iGeomIdx = INT_MAX;
     147             :     }
     148             : 
     149          19 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
     150             :     {
     151          11 :         if (IsSelectedGeomField(i))
     152             :         {
     153           9 :             const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
     154           9 :             poGeomFieldDefn->SetType(
     155           9 :                 !m_opts.m_type.empty()
     156             :                     ? m_opts.m_eType
     157           7 :                     : OGR_GT_GetSingle(poGeomFieldDefn->GetType()));
     158             :         }
     159             :     }
     160           8 : }
     161             : 
     162             : /************************************************************************/
     163             : /*                          TranslateFeature()                          */
     164             : /************************************************************************/
     165             : 
     166         413 : void GDALVectorExplodeCollectionsAlgorithmLayer::TranslateFeature(
     167             :     std::unique_ptr<OGRFeature> poSrcFeature,
     168             :     std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
     169             : {
     170         826 :     std::list<std::pair<std::unique_ptr<OGRFeature>, int>> apoTmpFeatures;
     171         413 :     apoTmpFeatures.emplace_back(std::move(poSrcFeature), 0);
     172         413 :     const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
     173         832 :     while (!apoTmpFeatures.empty())
     174             :     {
     175         838 :         auto [poCurFeature, nextGeomIndex] = std::move(apoTmpFeatures.front());
     176         419 :         auto insertionPoint = apoTmpFeatures.erase(apoTmpFeatures.begin());
     177         419 :         bool bInsertionDone = false;
     178         839 :         for (int i = nextGeomIndex; i < nGeomFieldCount; ++i)
     179             :         {
     180         423 :             auto poGeom = poCurFeature->GetGeomFieldRef(i);
     181         422 :             if (poGeom && !poGeom->IsEmpty() &&
     182         422 :                 OGR_GT_IsSubClassOf(poGeom->getGeometryType(),
     183         845 :                                     wkbGeometryCollection) &&
     184          20 :                 IsSelectedGeomField(i))
     185             :             {
     186             :                 const auto poGeomFieldDefn =
     187          16 :                     m_poFeatureDefn->GetGeomFieldDefn(i);
     188          16 :                 bInsertionDone = true;
     189             :                 const auto eTargetType =
     190          16 :                     !m_opts.m_type.empty()
     191          16 :                         ? m_opts.m_eType
     192          14 :                         : OGR_GT_GetSingle(poGeomFieldDefn->GetType());
     193             :                 auto poColl = std::unique_ptr<OGRGeometryCollection>(
     194          16 :                     poCurFeature->StealGeometry(i)->toGeometryCollection());
     195          16 :                 bool bTmpFeaturesInserted = false;
     196          49 :                 for (const auto *poSubGeomRef : poColl.get())
     197             :                 {
     198             :                     auto poNewFeature =
     199          66 :                         std::unique_ptr<OGRFeature>(poCurFeature->Clone());
     200             :                     auto poNewGeom =
     201          66 :                         std::unique_ptr<OGRGeometry>(poSubGeomRef->clone());
     202          33 :                     if (poNewGeom->getGeometryType() != eTargetType)
     203           6 :                         poNewGeom = OGRGeometryFactory::forceTo(
     204           6 :                             std::move(poNewGeom), eTargetType);
     205          36 :                     if (m_opts.m_skip && !m_opts.m_type.empty() &&
     206           3 :                         (!poNewGeom ||
     207           3 :                          (wkbFlatten(eTargetType) != wkbUnknown &&
     208           3 :                           poNewGeom->getGeometryType() != eTargetType)))
     209             :                     {
     210             :                         // skip
     211             :                     }
     212             :                     else
     213             :                     {
     214          64 :                         poNewGeom->assignSpatialReference(
     215          32 :                             poGeomFieldDefn->GetSpatialRef());
     216          32 :                         poNewFeature->SetGeomFieldDirectly(i,
     217             :                                                            poNewGeom.release());
     218             : 
     219          56 :                         if (!m_opts.m_geomField.empty() ||
     220          24 :                             i == nGeomFieldCount - 1)
     221             :                         {
     222          26 :                             poNewFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     223          26 :                             poNewFeature->SetFID(m_nextFID);
     224          26 :                             ++m_nextFID;
     225          26 :                             apoOutFeatures.push_back(std::move(poNewFeature));
     226             :                         }
     227             :                         else
     228             :                         {
     229           6 :                             bTmpFeaturesInserted = true;
     230             :                             apoTmpFeatures.insert(
     231             :                                 insertionPoint,
     232           6 :                                 std::pair<std::unique_ptr<OGRFeature>, int>(
     233           6 :                                     std::move(poNewFeature),
     234          12 :                                     nextGeomIndex + 1));
     235             :                         }
     236             :                     }
     237             :                 }
     238             : 
     239          16 :                 if (bTmpFeaturesInserted)
     240           3 :                     break;
     241             :             }
     242         407 :             else if (poGeom)
     243             :             {
     244             :                 const auto poGeomFieldDefn =
     245         406 :                     m_poFeatureDefn->GetGeomFieldDefn(i);
     246         406 :                 poGeom->assignSpatialReference(
     247         406 :                     poGeomFieldDefn->GetSpatialRef());
     248             :             }
     249             :         }
     250         419 :         if (!bInsertionDone)
     251             :         {
     252         403 :             poCurFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     253         403 :             poCurFeature->SetFID(m_nextFID);
     254         403 :             ++m_nextFID;
     255         403 :             apoOutFeatures.push_back(std::move(poCurFeature));
     256             :         }
     257             :     }
     258         413 : }
     259             : 
     260             : }  // namespace
     261             : 
     262             : /************************************************************************/
     263             : /*       GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer()        */
     264             : /************************************************************************/
     265             : 
     266             : std::unique_ptr<OGRLayerWithTranslateFeature>
     267           8 : GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer(OGRLayer &srcLayer)
     268             : {
     269           8 :     return std::make_unique<GDALVectorExplodeCollectionsAlgorithmLayer>(
     270           8 :         srcLayer, m_opts);
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*           GDALVectorExplodeCollectionsAlgorithm::RunStep()           */
     275             : /************************************************************************/
     276             : 
     277           9 : bool GDALVectorExplodeCollectionsAlgorithm::RunStep(
     278             :     GDALPipelineStepRunContext &ctxt)
     279             : {
     280           9 :     if (!m_opts.m_type.empty())
     281             :     {
     282           3 :         m_opts.m_eType = OGRFromOGCGeomType(m_opts.m_type.c_str());
     283           4 :         if (wkbFlatten(m_opts.m_eType) == wkbUnknown &&
     284           1 :             !STARTS_WITH_CI(m_opts.m_type.c_str(), "GEOMETRY"))
     285             :         {
     286           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     287             :                         "Invalid geometry type '%s'", m_opts.m_type.c_str());
     288           1 :             return false;
     289             :         }
     290             :     }
     291             : 
     292           8 :     return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
     293             : }
     294             : 
     295             : GDALVectorExplodeCollectionsAlgorithmStandalone::
     296             :     ~GDALVectorExplodeCollectionsAlgorithmStandalone() = default;
     297             : 
     298             : //! @endcond

Generated by: LCOV version 1.14