LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_explode_collections.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 122 123 99.2 %
Date: 2026-06-23 16:35:19 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          91 : GDALVectorExplodeCollectionsAlgorithm::GDALVectorExplodeCollectionsAlgorithm(
      32          91 :     bool standaloneStep)
      33             :     : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
      34          91 :                                       standaloneStep, m_opts)
      35             : {
      36         182 :     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          91 :             });
      57             : 
      58             :     AddArg("skip-on-type-mismatch", 0,
      59             :            _("Skip feature when change of feature geometry type failed"),
      60          91 :            &m_opts.m_skip);
      61          91 : }
      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             :     bool TranslateFeature(
      82             :         std::unique_ptr<OGRFeature> poSrcFeature,
      83             :         std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
      84             : 
      85         433 :     bool IsSelectedGeomField(int idx) const
      86             :     {
      87         433 :         return m_iGeomIdx < 0 || idx == m_iGeomIdx;
      88             :     }
      89             : 
      90             :   public:
      91             :     GDALVectorExplodeCollectionsAlgorithmLayer(
      92             :         OGRLayer &oSrcLayer,
      93             :         const GDALVectorExplodeCollectionsAlgorithm::Options &opts);
      94             : 
      95         234 :     const OGRFeatureDefn *GetLayerDefn() const override
      96             :     {
      97         234 :         return m_poFeatureDefn.get();
      98             :     }
      99             : 
     100         122 :     void ResetReading() override
     101             :     {
     102         122 :         m_nextFID = 1;
     103         122 :         GDALVectorPipelineOutputLayer::ResetReading();
     104         122 :     }
     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          41 :     int TestCapability(const char *pszCap) const override
     113             :     {
     114          41 :         if (EQUAL(pszCap, OLCCurveGeometries) ||
     115          37 :             EQUAL(pszCap, OLCMeasuredGeometries) ||
     116          33 :             EQUAL(pszCap, OLCZGeometries) || EQUAL(pszCap, OLCFastGetExtent) ||
     117          28 :             EQUAL(pszCap, OLCStringsAsUTF8))
     118             :         {
     119          28 :             return m_srcLayer.TestCapability(pszCap);
     120             :         }
     121          13 :         return false;
     122             :     }
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /*             GDALVectorExplodeCollectionsAlgorithmLayer()             */
     127             : /************************************************************************/
     128             : 
     129           9 : GDALVectorExplodeCollectionsAlgorithmLayer::
     130             :     GDALVectorExplodeCollectionsAlgorithmLayer(
     131             :         OGRLayer &oSrcLayer,
     132           9 :         const GDALVectorExplodeCollectionsAlgorithm::Options &opts)
     133             :     : GDALVectorPipelineOutputLayer(oSrcLayer), m_opts(opts),
     134           9 :       m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
     135             : {
     136           9 :     SetDescription(oSrcLayer.GetDescription());
     137           9 :     SetMetadata(oSrcLayer.GetMetadata());
     138             : 
     139           9 :     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          21 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
     150             :     {
     151          12 :         if (IsSelectedGeomField(i))
     152             :         {
     153          10 :             const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
     154          10 :             poGeomFieldDefn->SetType(
     155          10 :                 !m_opts.m_type.empty()
     156             :                     ? m_opts.m_eType
     157           8 :                     : OGR_GT_GetSingle(poGeomFieldDefn->GetType()));
     158             :         }
     159             :     }
     160           9 : }
     161             : 
     162             : /************************************************************************/
     163             : /*                          TranslateFeature()                          */
     164             : /************************************************************************/
     165             : 
     166         415 : bool GDALVectorExplodeCollectionsAlgorithmLayer::TranslateFeature(
     167             :     std::unique_ptr<OGRFeature> poSrcFeature,
     168             :     std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
     169             : {
     170         415 :     std::list<std::pair<std::unique_ptr<OGRFeature>, int>> apoTmpFeatures;
     171         415 :     apoTmpFeatures.emplace_back(std::move(poSrcFeature), 0);
     172         415 :     const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
     173         836 :     while (!apoTmpFeatures.empty())
     174             :     {
     175         842 :         auto [poCurFeature, nextGeomIndex] = std::move(apoTmpFeatures.front());
     176         421 :         auto insertionPoint = apoTmpFeatures.erase(apoTmpFeatures.begin());
     177         421 :         bool bInsertionDone = false;
     178         843 :         for (int i = nextGeomIndex; i < nGeomFieldCount; ++i)
     179             :         {
     180         425 :             auto poGeom = poCurFeature->GetGeomFieldRef(i);
     181         424 :             if (poGeom && !poGeom->IsEmpty() &&
     182         424 :                 OGR_GT_IsSubClassOf(poGeom->getGeometryType(),
     183         849 :                                     wkbGeometryCollection) &&
     184         421 :                 IsSelectedGeomField(i))
     185             :             {
     186             :                 const auto poGeomFieldDefn =
     187         417 :                     m_poFeatureDefn->GetGeomFieldDefn(i);
     188         417 :                 bInsertionDone = true;
     189             :                 const auto eTargetType =
     190         417 :                     !m_opts.m_type.empty()
     191         417 :                         ? m_opts.m_eType
     192         415 :                         : OGR_GT_GetSingle(poGeomFieldDefn->GetType());
     193             :                 auto poColl = std::unique_ptr<OGRGeometryCollection>(
     194         417 :                     poCurFeature->StealGeometry(i)->toGeometryCollection());
     195         417 :                 bool bTmpFeaturesInserted = false;
     196         851 :                 for (const auto *poSubGeomRef : poColl.get())
     197             :                 {
     198             :                     auto poNewFeature =
     199         868 :                         std::unique_ptr<OGRFeature>(poCurFeature->Clone());
     200             :                     auto poNewGeom =
     201         868 :                         std::unique_ptr<OGRGeometry>(poSubGeomRef->clone());
     202         434 :                     if (poNewGeom->getGeometryType() != eTargetType)
     203           6 :                         poNewGeom = OGRGeometryFactory::forceTo(
     204           6 :                             std::move(poNewGeom), eTargetType);
     205         437 :                     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         866 :                         poNewGeom->assignSpatialReference(
     215         433 :                             poGeomFieldDefn->GetSpatialRef());
     216         433 :                         poNewFeature->SetGeomFieldDirectly(i,
     217             :                                                            poNewGeom.release());
     218             : 
     219         858 :                         if (!m_opts.m_geomField.empty() ||
     220         425 :                             i == nGeomFieldCount - 1)
     221             :                         {
     222         427 :                             poNewFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     223         427 :                             poNewFeature->SetFID(m_nextFID);
     224         427 :                             ++m_nextFID;
     225         427 :                             if (PassesFilters(poNewFeature.get()))
     226         344 :                                 apoOutFeatures.push_back(
     227         344 :                                     std::move(poNewFeature));
     228             :                         }
     229             :                         else
     230             :                         {
     231           6 :                             bTmpFeaturesInserted = true;
     232             :                             apoTmpFeatures.insert(
     233             :                                 insertionPoint,
     234           6 :                                 std::pair<std::unique_ptr<OGRFeature>, int>(
     235           6 :                                     std::move(poNewFeature),
     236          12 :                                     nextGeomIndex + 1));
     237             :                         }
     238             :                     }
     239             :                 }
     240             : 
     241         417 :                 if (bTmpFeaturesInserted)
     242           3 :                     break;
     243             :             }
     244           8 :             else if (poGeom)
     245             :             {
     246             :                 const auto poGeomFieldDefn =
     247           7 :                     m_poFeatureDefn->GetGeomFieldDefn(i);
     248           7 :                 poGeom->assignSpatialReference(
     249           7 :                     poGeomFieldDefn->GetSpatialRef());
     250             :             }
     251             :         }
     252         421 :         if (!bInsertionDone)
     253             :         {
     254           4 :             poCurFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     255           4 :             poCurFeature->SetFID(m_nextFID);
     256           4 :             ++m_nextFID;
     257           4 :             if (PassesFilters(poCurFeature.get()))
     258           3 :                 apoOutFeatures.push_back(std::move(poCurFeature));
     259             :         }
     260             :     }
     261             : 
     262         830 :     return true;
     263             : }
     264             : 
     265             : }  // namespace
     266             : 
     267             : /************************************************************************/
     268             : /*       GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer()        */
     269             : /************************************************************************/
     270             : 
     271             : std::unique_ptr<OGRLayerWithTranslateFeature>
     272           9 : GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer(OGRLayer &srcLayer)
     273             : {
     274           9 :     return std::make_unique<GDALVectorExplodeCollectionsAlgorithmLayer>(
     275           9 :         srcLayer, m_opts);
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*           GDALVectorExplodeCollectionsAlgorithm::RunStep()           */
     280             : /************************************************************************/
     281             : 
     282          10 : bool GDALVectorExplodeCollectionsAlgorithm::RunStep(
     283             :     GDALPipelineStepRunContext &ctxt)
     284             : {
     285          10 :     if (!m_opts.m_type.empty())
     286             :     {
     287           3 :         m_opts.m_eType = OGRFromOGCGeomType(m_opts.m_type.c_str());
     288           4 :         if (wkbFlatten(m_opts.m_eType) == wkbUnknown &&
     289           1 :             !STARTS_WITH_CI(m_opts.m_type.c_str(), "GEOMETRY"))
     290             :         {
     291           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     292             :                         "Invalid geometry type '%s'", m_opts.m_type.c_str());
     293           1 :             return false;
     294             :         }
     295             :     }
     296             : 
     297           9 :     return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
     298             : }
     299             : 
     300             : GDALVectorExplodeCollectionsAlgorithmStandalone::
     301             :     ~GDALVectorExplodeCollectionsAlgorithmStandalone() = default;
     302             : 
     303             : //! @endcond

Generated by: LCOV version 1.14