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

Generated by: LCOV version 1.14