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

Generated by: LCOV version 1.14