LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_dissolve.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 59 80 73.8 %
Date: 2026-03-13 03:16:58 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "gdal vector dissolve"
       5             :  * Author:   Dan Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_dissolve.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "ogrsf_frmts.h"
      17             : 
      18             : #include <cinttypes>
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*                    GDALVectorDissolveAlgorithm()                     */
      28             : /************************************************************************/
      29             : 
      30          46 : GDALVectorDissolveAlgorithm::GDALVectorDissolveAlgorithm(bool standaloneStep)
      31             :     : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
      32          46 :                                       standaloneStep, m_opts)
      33             : {
      34          46 : }
      35             : 
      36             : #ifdef HAVE_GEOS
      37             : 
      38             : namespace
      39             : {
      40             : 
      41             : /************************************************************************/
      42             : /*                   GDALVectorDissolveAlgorithmLayer                   */
      43             : /************************************************************************/
      44             : 
      45             : class GDALVectorDissolveAlgorithmLayer final
      46             :     : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorDissolveAlgorithm>
      47             : {
      48             :   public:
      49           4 :     GDALVectorDissolveAlgorithmLayer(
      50             :         OGRLayer &oSrcLayer, const GDALVectorDissolveAlgorithm::Options &opts)
      51           4 :         : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorDissolveAlgorithm>(
      52           4 :               oSrcLayer, opts)
      53             :     {
      54           4 :     }
      55             : 
      56             :   protected:
      57             :     using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
      58             : 
      59             :     std::unique_ptr<OGRFeature>
      60             :     TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
      61             : 
      62             :   private:
      63             : };
      64             : 
      65             : /************************************************************************/
      66             : /*                          TranslateFeature()                          */
      67             : /************************************************************************/
      68             : 
      69           2 : std::unique_ptr<OGRGeometry> LineMerge(const OGRMultiLineString *poGeom)
      70             : {
      71           2 :     GEOSContextHandle_t hContext = OGRGeometry::createGEOSContext();
      72           2 :     GEOSGeometry *hGeosGeom = poGeom->exportToGEOS(hContext);
      73           2 :     if (!hGeosGeom)
      74             :     {
      75           0 :         OGRGeometry::freeGEOSContext(hContext);
      76           0 :         return nullptr;
      77             :     }
      78             : 
      79           2 :     GEOSGeometry *hGeosResult = GEOSLineMerge_r(hContext, hGeosGeom);
      80           2 :     GEOSGeom_destroy_r(hContext, hGeosGeom);
      81             : 
      82           2 :     if (!hGeosResult)
      83             :     {
      84           0 :         OGRGeometry::freeGEOSContext(hContext);
      85           0 :         return nullptr;
      86             :     }
      87             : 
      88             :     std::unique_ptr<OGRGeometry> ret(
      89           4 :         OGRGeometryFactory::createFromGEOS(hContext, hGeosResult));
      90           2 :     GEOSGeom_destroy_r(hContext, hGeosResult);
      91           2 :     OGRGeometry::freeGEOSContext(hContext);
      92             : 
      93           2 :     if (ret)
      94             :     {
      95           2 :         const auto eRetType = wkbFlatten(ret->getGeometryType());
      96           2 :         if (eRetType != wkbLineString && eRetType != wkbMultiLineString)
      97             :         {
      98           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      99             :                      "LineMerge returned a geometry of type %s, expected "
     100             :                      "LineString or MultiLineString",
     101             :                      OGRGeometryTypeToName(eRetType));
     102           0 :             return nullptr;
     103             :         }
     104             :     }
     105             : 
     106           2 :     return ret;
     107             : }
     108             : 
     109           4 : std::unique_ptr<OGRFeature> GDALVectorDissolveAlgorithmLayer::TranslateFeature(
     110             :     std::unique_ptr<OGRFeature> poSrcFeature) const
     111             : {
     112           4 :     const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount();
     113           8 :     for (int iGeomField = 0; iGeomField < nGeomFieldCount; ++iGeomField)
     114             :     {
     115           4 :         if (IsSelectedGeomField(iGeomField))
     116             :         {
     117           4 :             if (auto poGeom = std::unique_ptr<OGRGeometry>(
     118           4 :                     poSrcFeature->StealGeometry(iGeomField)))
     119             :             {
     120           4 :                 poGeom.reset(poGeom->UnaryUnion());
     121           4 :                 if (!poGeom)
     122             :                 {
     123           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     124             :                              "Failed to perform union of geometry on feature "
     125             :                              "%" PRId64,
     126           0 :                              static_cast<int64_t>(poSrcFeature->GetFID()));
     127           0 :                     return nullptr;
     128             :                 }
     129             : 
     130           4 :                 const auto eResultType = wkbFlatten(poGeom->getGeometryType());
     131             : 
     132           4 :                 if (eResultType == wkbMultiLineString)
     133             :                 {
     134           1 :                     poGeom = LineMerge(poGeom->toMultiLineString());
     135           1 :                     if (!poGeom)
     136             :                     {
     137           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     138             :                                  "Failed to merge lines of feature %" PRId64,
     139           0 :                                  static_cast<int64_t>(poSrcFeature->GetFID()));
     140           0 :                         return nullptr;
     141             :                     }
     142             :                 }
     143           3 :                 else if (eResultType == wkbGeometryCollection)
     144             :                 {
     145             :                     OGRGeometryCollection *poColl =
     146           1 :                         poGeom->toGeometryCollection();
     147             : 
     148           1 :                     OGRMultiLineString oMLS;
     149             : 
     150           1 :                     const auto nGeoms = poColl->getNumGeometries();
     151           4 :                     for (int i = nGeoms - 1; i >= 0; i--)
     152             :                     {
     153           3 :                         const auto eComponentType = wkbFlatten(
     154             :                             poColl->getGeometryRef(i)->getGeometryType());
     155           3 :                         if (eComponentType == wkbLineString)
     156             :                         {
     157           4 :                             oMLS.addGeometryDirectly(poColl->stealGeometry(i)
     158             :                                                          .release()
     159           2 :                                                          ->toLineString());
     160             :                         }
     161             :                     }
     162             : 
     163           1 :                     if (oMLS.getNumGeometries() > 0)
     164             :                     {
     165             :                         std::unique_ptr<OGRGeometry> poMerged =
     166           1 :                             LineMerge(&oMLS);
     167           1 :                         if (!poMerged)
     168             :                         {
     169           0 :                             CPLError(
     170             :                                 CE_Failure, CPLE_AppDefined,
     171             :                                 "Failed to merge lines of feature %" PRId64,
     172           0 :                                 static_cast<int64_t>(poSrcFeature->GetFID()));
     173           0 :                             return nullptr;
     174             :                         }
     175             : 
     176             :                         const auto eMergedType =
     177           1 :                             wkbFlatten(poMerged->getGeometryType());
     178           1 :                         if (eMergedType == wkbLineString)
     179             :                         {
     180           1 :                             poColl->addGeometry(std::move(poMerged));
     181             :                         }
     182             :                         else  // eMergedType == wkbMultiLineString
     183             :                         {
     184             :                             OGRMultiLineString *poMergedMLS =
     185           0 :                                 poMerged->toMultiLineString();
     186             :                             const auto nMergedGeoms =
     187           0 :                                 poMergedMLS->getNumGeometries();
     188           0 :                             for (int i = nMergedGeoms - 1; i >= 0; i--)
     189             :                             {
     190           0 :                                 poColl->addGeometryDirectly(
     191           0 :                                     poMergedMLS->stealGeometry(i)
     192             :                                         .release()
     193           0 :                                         ->toLineString());
     194             :                             }
     195             :                         }
     196             :                     }
     197             :                 }
     198             : 
     199           4 :                 if (poGeom)
     200             :                 {
     201           8 :                     poGeom->assignSpatialReference(
     202           8 :                         m_srcLayer.GetLayerDefn()
     203           4 :                             ->GetGeomFieldDefn(iGeomField)
     204           8 :                             ->GetSpatialRef());
     205           4 :                     poSrcFeature->SetGeomField(iGeomField, std::move(poGeom));
     206             :                 }
     207             :             }
     208             :         }
     209             :     }
     210             : 
     211           4 :     return poSrcFeature;
     212             : }
     213             : 
     214             : }  // namespace
     215             : 
     216             : #endif  // HAVE_GEOS
     217             : 
     218             : /************************************************************************/
     219             : /*            GDALVectorDissolveAlgorithm::CreateAlgLayer()             */
     220             : /************************************************************************/
     221             : 
     222             : std::unique_ptr<OGRLayerWithTranslateFeature>
     223           4 : GDALVectorDissolveAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
     224             : {
     225             : #ifdef HAVE_GEOS
     226           4 :     return std::make_unique<GDALVectorDissolveAlgorithmLayer>(srcLayer, m_opts);
     227             : #else
     228             :     CPLAssert(false);
     229             :     return nullptr;
     230             : #endif
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                GDALVectorDissolveAlgorithm::RunStep()                */
     235             : /************************************************************************/
     236             : 
     237           4 : bool GDALVectorDissolveAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     238             : {
     239             : #ifdef HAVE_GEOS
     240           4 :     return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
     241             : #else
     242             :     (void)ctxt;
     243             :     ReportError(CE_Failure, CPLE_NotSupported,
     244             :                 "This algorithm is only supported for builds against GEOS");
     245             :     return false;
     246             : #endif
     247             : }
     248             : 
     249             : GDALVectorDissolveAlgorithmStandalone::
     250             :     ~GDALVectorDissolveAlgorithmStandalone() = default;
     251             : 
     252             : //! @endcond

Generated by: LCOV version 1.14