LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_geom_explode_collections.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 123 124 99.2 %
Date: 2025-03-31 11:17:27 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.14