LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_polygonize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 75 78 96.2 %
Date: 2026-06-17 01:50:11 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster polygonize" subcommand
       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_raster_polygonize.h"
      14             : #include "gdalalg_vector_write.h"
      15             : 
      16             : #include "cpl_conv.h"
      17             : #include "gdal_priv.h"
      18             : #include "gdal_alg.h"
      19             : #include "ogrsf_frmts.h"
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*    GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm()    */
      29             : /************************************************************************/
      30             : 
      31          94 : GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm(
      32          94 :     bool standaloneStep)
      33             :     : GDALPipelineStepAlgorithm(
      34             :           NAME, DESCRIPTION, HELP_URL,
      35           0 :           ConstructorOptions()
      36          94 :               .SetStandaloneStep(standaloneStep)
      37          94 :               .SetAddUpsertArgument(false)
      38          94 :               .SetAddSkipErrorsArgument(false)
      39          94 :               .SetOutputLayerNameAvailableInPipelineStep(true)
      40         188 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      41             : {
      42          94 :     if (standaloneStep)
      43             :     {
      44          68 :         AddProgressArg();
      45          68 :         AddRasterInputArgs(false, false);
      46          68 :         AddVectorOutputArgs(false, false);
      47             :     }
      48             :     else
      49             :     {
      50          26 :         AddRasterHiddenInputDatasetArg();
      51          26 :         AddOutputLayerNameArg(/* hiddenForCLI = */ false,
      52             :                               /* shortNameOutputLayerAllowed = */ false);
      53             :     }
      54             : 
      55             :     // gdal_polygonize specific options
      56          94 :     AddBandArg(&m_band).SetDefault(m_band);
      57             :     AddArg("attribute-name", 0, _("Name of the field with the pixel value"),
      58         188 :            &m_attributeName)
      59          94 :         .SetDefault(m_attributeName);
      60             : 
      61             :     AddArg("connect-diagonal-pixels", 'c',
      62         188 :            _("Consider diagonal pixels as connected"), &m_connectDiagonalPixels)
      63          94 :         .SetDefault(m_connectDiagonalPixels);
      64             : 
      65         188 :     AddArg("commit-interval", 0, _("Commit interval"), &m_commitInterval)
      66          94 :         .SetHidden();
      67          94 : }
      68             : 
      69           4 : bool GDALRasterPolygonizeAlgorithm::CanHandleNextStep(
      70             :     GDALPipelineStepAlgorithm *poNextStep) const
      71             : {
      72           7 :     return poNextStep->GetName() == GDALVectorWriteAlgorithm::NAME &&
      73           7 :            poNextStep->GetOutputFormat() != "stream";
      74             : }
      75             : 
      76             : /************************************************************************/
      77             : /*               GDALRasterPolygonizeAlgorithm::RunImpl()               */
      78             : /************************************************************************/
      79             : 
      80          22 : bool GDALRasterPolygonizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
      81             :                                             void *pProgressData)
      82             : {
      83          22 :     GDALPipelineStepRunContext stepCtxt;
      84          22 :     stepCtxt.m_pfnProgress = pfnProgress;
      85          22 :     stepCtxt.m_pProgressData = pProgressData;
      86          22 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
      87             : }
      88             : 
      89             : /************************************************************************/
      90             : /*               GDALRasterPolygonizeAlgorithm::RunStep()               */
      91             : /************************************************************************/
      92             : 
      93          28 : bool GDALRasterPolygonizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
      94             : {
      95          28 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      96          28 :     CPLAssert(poSrcDS);
      97             : 
      98          28 :     auto poWriteStep = ctxt.m_poNextUsableStep ? ctxt.m_poNextUsableStep : this;
      99             : 
     100          28 :     GDALDataset *poDstDS = nullptr;
     101          28 :     bool bTemporaryFile = false;
     102          28 :     std::unique_ptr<GDALDataset> poNewRetDS;
     103          56 :     std::string outputLayerName;
     104          28 :     OGRLayer *poDstLayer = nullptr;
     105          28 :     if (!CreateDatasetSingleOutputLayerIfNeeded(ctxt, "polygonize", poDstDS,
     106             :                                                 bTemporaryFile, poNewRetDS,
     107             :                                                 outputLayerName, poDstLayer))
     108             :     {
     109           4 :         return false;
     110             :     }
     111             : 
     112          24 :     auto poSrcBand = poSrcDS->GetRasterBand(m_band);
     113          24 :     const auto eDT = poSrcBand->GetRasterDataType();
     114             : 
     115          24 :     if (!poDstLayer)
     116             :     {
     117          22 :         poDstLayer = poDstDS->CreateLayer(
     118          22 :             outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
     119          44 :             CPLStringList(poWriteStep->GetLayerCreationOptions()).List());
     120          22 :         if (!poDstLayer)
     121             :         {
     122           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
     123             :                         outputLayerName.c_str());
     124           1 :             return false;
     125             :         }
     126             : 
     127             :         OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
     128          21 :                                 !GDALDataTypeIsInteger(eDT) ? OFTReal
     129          19 :                                 : eDT == GDT_Int64 || eDT == GDT_UInt64
     130          39 :                                     ? OFTInteger64
     131          41 :                                     : OFTInteger);
     132          21 :         if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
     133             :         {
     134           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     135             :                         "Cannot create field '%s' in layer '%s'",
     136             :                         m_attributeName.c_str(), outputLayerName.c_str());
     137           0 :             return false;
     138             :         }
     139             :     }
     140             : 
     141             :     const int iPixValField =
     142          23 :         poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
     143          23 :     if (iPixValField < 0)
     144             :     {
     145           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     146             :                     "Cannot find field '%s' in layer '%s'",
     147             :                     m_attributeName.c_str(), outputLayerName.c_str());
     148           1 :         return false;
     149             :     }
     150             : 
     151          22 :     CPLStringList aosPolygonizeOptions;
     152          22 :     if (m_connectDiagonalPixels)
     153             :     {
     154           1 :         aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
     155             :     }
     156          22 :     if (m_commitInterval)
     157             :     {
     158             :         aosPolygonizeOptions.SetNameValue("COMMIT_INTERVAL",
     159           2 :                                           CPLSPrintf("%d", m_commitInterval));
     160             :     }
     161             : 
     162             :     bool ret;
     163          22 :     if (GDALDataTypeIsInteger(eDT))
     164             :     {
     165          42 :         ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     166          21 :                              GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     167             :                              OGRLayer::ToHandle(poDstLayer), iPixValField,
     168             :                              aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     169             :                              ctxt.m_pProgressData) == CE_None;
     170             :     }
     171             :     else
     172             :     {
     173           1 :         ret =
     174           2 :             GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     175           1 :                             GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     176             :                             OGRLayer::ToHandle(poDstLayer), iPixValField,
     177             :                             aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     178             :                             ctxt.m_pProgressData) == CE_None;
     179             :     }
     180             : 
     181          22 :     if (ret && poNewRetDS)
     182             :     {
     183          20 :         if (bTemporaryFile)
     184             :         {
     185           3 :             ret = poNewRetDS->FlushCache() == CE_None;
     186             : #if !defined(__APPLE__)
     187             :             // For some unknown reason, unlinking the file on MacOSX
     188             :             // leads to later "disk I/O error". See https://github.com/OSGeo/gdal/issues/13794
     189           3 :             VSIUnlink(poNewRetDS->GetDescription());
     190             : #endif
     191             :         }
     192             : 
     193          20 :         m_outputDataset.Set(std::move(poNewRetDS));
     194             :     }
     195             : 
     196          22 :     return ret;
     197             : }
     198             : 
     199             : GDALRasterPolygonizeAlgorithmStandalone::
     200             :     ~GDALRasterPolygonizeAlgorithmStandalone() = default;
     201             : 
     202             : //! @endcond

Generated by: LCOV version 1.14