LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_check_coverage.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 72 77 93.5 %
Date: 2026-02-01 11:59:10 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             : *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "gdal vector check-coverage" subcommand
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_check_coverage.h"
      14             : 
      15             : #include "cpl_error.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdalalg_vector_geom.h"
      18             : #include "ogr_geometry.h"
      19             : #include "ogr_geos.h"
      20             : 
      21             : #include <cinttypes>
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : //! @cond Doxygen_Suppress
      28             : 
      29          51 : GDALVectorCheckCoverageAlgorithm::GDALVectorCheckCoverageAlgorithm(
      30          51 :     bool standaloneStep)
      31             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      32          51 :                                       standaloneStep)
      33             : {
      34             :     AddArg("include-valid", 0,
      35             :            _("Include valid inputs in output, with empty geometry"),
      36          51 :            &m_includeValid);
      37             : 
      38             :     AddArg("geometry-field", 0, _("Name of geometry field to check"),
      39          51 :            &m_geomField);
      40             : 
      41             :     AddArg("maximum-gap-width", 0, _("Maximum width of a gap to be flagged"),
      42         102 :            &m_maximumGapWidth)
      43          51 :         .SetMinValueIncluded(0);
      44          51 : }
      45             : 
      46             : #if defined HAVE_GEOS &&                                                       \
      47             :     (GEOS_VERSION_MAJOR > 3 ||                                                 \
      48             :      (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12))
      49             : 
      50             : class GDALVectorCheckCoverageOutputLayer final
      51             :     : public GDALGeosNonStreamingAlgorithmLayer
      52             : {
      53             :   public:
      54           8 :     explicit GDALVectorCheckCoverageOutputLayer(OGRLayer &srcLayer,
      55             :                                                 int geomFieldIndex,
      56             :                                                 const std::string &name,
      57             :                                                 double maximumGapWidth,
      58             :                                                 bool includeValid)
      59           8 :         : GDALGeosNonStreamingAlgorithmLayer(srcLayer, geomFieldIndex),
      60           8 :           m_defn(OGRFeatureDefn::CreateFeatureDefn(name.c_str())),
      61           8 :           m_maximumGapWidth(maximumGapWidth), m_includeValid(includeValid)
      62             :     {
      63           8 :         m_defn->Reference();
      64             : 
      65           8 :         const OGRFeatureDefn *poSrcLayerDefn = srcLayer.GetLayerDefn();
      66           8 :         m_defn->SetGeomType(wkbMultiLineString);
      67          16 :         m_defn->GetGeomFieldDefn(0)->SetSpatialRef(
      68           8 :             poSrcLayerDefn->GetGeomFieldDefn(geomFieldIndex)->GetSpatialRef());
      69           8 :     }
      70             : 
      71             :     ~GDALVectorCheckCoverageOutputLayer() override;
      72             : 
      73          87 :     const OGRFeatureDefn *GetLayerDefn() const override
      74             :     {
      75          87 :         return m_defn;
      76             :     }
      77             : 
      78           0 :     int TestCapability(const char *) const override
      79             :     {
      80           0 :         return false;
      81             :     }
      82             : 
      83          32 :     bool PolygonsOnly() const override
      84             :     {
      85          32 :         return true;
      86             :     }
      87             : 
      88          32 :     bool SkipEmpty() const override
      89             :     {
      90          32 :         return !m_includeValid;
      91             :     }
      92             : 
      93           8 :     bool ProcessGeos() override
      94             :     {
      95             :         // Perform coverage checking
      96           8 :         GEOSGeometry *coll = GEOSGeom_createCollection_r(
      97             :             m_poGeosContext, GEOS_GEOMETRYCOLLECTION, m_apoGeosInputs.data(),
      98           8 :             static_cast<unsigned int>(m_apoGeosInputs.size()));
      99             : 
     100           8 :         if (coll == nullptr)
     101             :         {
     102           0 :             return false;
     103             :         }
     104             : 
     105           8 :         m_apoGeosInputs.clear();
     106             : 
     107             :         int geos_result =
     108           8 :             GEOSCoverageIsValid_r(m_poGeosContext, coll, m_maximumGapWidth,
     109             :                                   &m_poGeosResultAsCollection);
     110           8 :         GEOSGeom_destroy_r(m_poGeosContext, coll);
     111             : 
     112           8 :         CPLDebug("CoverageIsValid", "%d", geos_result);
     113             : 
     114           8 :         return geos_result != 2;
     115             :     }
     116             : 
     117             :   private:
     118             :     OGRFeatureDefn *m_defn;
     119             :     const double m_maximumGapWidth;
     120             :     const bool m_includeValid;
     121             : 
     122             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorCheckCoverageOutputLayer)
     123             : };
     124             : 
     125          16 : GDALVectorCheckCoverageOutputLayer::~GDALVectorCheckCoverageOutputLayer()
     126             : {
     127           8 :     if (m_defn != nullptr)
     128             :     {
     129           8 :         m_defn->Release();
     130             :     }
     131          16 : }
     132             : 
     133           9 : bool GDALVectorCheckCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     134             : {
     135           9 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     136          18 :     auto poDstDS = std::make_unique<GDALVectorNonStreamingAlgorithmDataset>();
     137             : 
     138           9 :     const bool bSingleLayerOutput = m_inputLayerNames.empty()
     139          12 :                                         ? poSrcDS->GetLayerCount() == 1
     140           3 :                                         : m_inputLayerNames.size() == 1;
     141             : 
     142          18 :     GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
     143             : 
     144          19 :     for (auto &&poSrcLayer : poSrcDS->GetLayers())
     145             :     {
     146          15 :         if (m_inputLayerNames.empty() ||
     147           0 :             std::find(m_inputLayerNames.begin(), m_inputLayerNames.end(),
     148          15 :                       poSrcLayer->GetDescription()) != m_inputLayerNames.end())
     149             :         {
     150          11 :             const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
     151          11 :             if (poSrcLayerDefn->GetGeomFieldCount() == 0)
     152             :             {
     153           2 :                 if (m_inputLayerNames.empty())
     154           1 :                     continue;
     155           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     156             :                             "Specified layer '%s' has no geometry field",
     157           1 :                             poSrcLayer->GetDescription());
     158           1 :                 return false;
     159             :             }
     160             : 
     161           9 :             progressHelper.AddProcessedLayer(*poSrcLayer);
     162             :         }
     163             :     }
     164             : 
     165          17 :     for ([[maybe_unused]] auto [poSrcLayer, bProcessed, layerProgressFunc,
     166          33 :                                 layerProgressData] : progressHelper)
     167             :     {
     168           9 :         const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
     169             :         const int geomFieldIndex =
     170           9 :             m_geomField.empty()
     171           9 :                 ? 0
     172           2 :                 : poSrcLayerDefn->GetGeomFieldIndex(m_geomField.c_str());
     173             : 
     174           9 :         if (geomFieldIndex == -1)
     175             :         {
     176           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     177             :                         "Specified geometry field '%s' does not exist in "
     178             :                         "layer '%s'",
     179           1 :                         m_geomField.c_str(), poSrcLayer->GetDescription());
     180           1 :             return false;
     181             :         }
     182             : 
     183             :         std::string layerName = bSingleLayerOutput
     184             :                                     ? "invalid_edge"
     185          16 :                                     : std::string("invalid_edge_")
     186          20 :                                           .append(poSrcLayer->GetDescription());
     187             : 
     188             :         auto poLayer = std::make_unique<GDALVectorCheckCoverageOutputLayer>(
     189           8 :             *poSrcLayer, geomFieldIndex, layerName, m_maximumGapWidth,
     190           8 :             m_includeValid);
     191             : 
     192           8 :         if (!poDstDS->AddProcessedLayer(std::move(poLayer), layerProgressFunc,
     193             :                                         layerProgressData.get()))
     194             :         {
     195           0 :             return false;
     196             :         }
     197             :     }
     198             : 
     199           7 :     m_outputDataset.Set(std::move(poDstDS));
     200             : 
     201           7 :     return true;
     202             : }
     203             : 
     204             : #else
     205             : 
     206             : bool GDALVectorCheckCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &)
     207             : {
     208             :     ReportError(CE_Failure, CPLE_AppDefined,
     209             :                 "%s requires GDAL to be built against version 3.12 or later of "
     210             :                 "the GEOS library.",
     211             :                 NAME);
     212             :     return false;
     213             : }
     214             : #endif  // HAVE_GEOS
     215             : 
     216             : GDALVectorCheckCoverageAlgorithmStandalone::
     217             :     ~GDALVectorCheckCoverageAlgorithmStandalone() = default;
     218             : 
     219             : //! @endcond

Generated by: LCOV version 1.14