LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_polygonize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 120 128 93.8 %
Date: 2025-10-19 15:46:27 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          42 : GDALRasterPolygonizeAlgorithm::GDALRasterPolygonizeAlgorithm(
      32          42 :     bool standaloneStep)
      33             :     : GDALPipelineStepAlgorithm(
      34             :           NAME, DESCRIPTION, HELP_URL,
      35           0 :           ConstructorOptions()
      36          42 :               .SetStandaloneStep(standaloneStep)
      37          42 :               .SetAddUpsertArgument(false)
      38          42 :               .SetAddSkipErrorsArgument(false)
      39          84 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      40             : {
      41          42 :     m_outputLayerName = "polygonize";
      42             : 
      43          42 :     AddProgressArg();
      44          42 :     if (standaloneStep)
      45             :     {
      46          23 :         AddRasterInputArgs(false, false);
      47          23 :         AddVectorOutputArgs(false, false);
      48             :     }
      49             : 
      50             :     // gdal_polygonize specific options
      51          42 :     AddBandArg(&m_band).SetDefault(m_band);
      52             :     AddArg("attribute-name", 0, _("Name of the field with the pixel value"),
      53          84 :            &m_attributeName)
      54          42 :         .SetDefault(m_attributeName);
      55             : 
      56             :     AddArg("connect-diagonal-pixels", 'c',
      57          84 :            _("Consider diagonal pixels as connected"), &m_connectDiagonalPixels)
      58          42 :         .SetDefault(m_connectDiagonalPixels);
      59             : 
      60          84 :     AddArg("commit-interval", 0, _("Commit interval"), &m_commitInterval)
      61          42 :         .SetHidden();
      62          42 : }
      63             : 
      64           3 : bool GDALRasterPolygonizeAlgorithm::CanHandleNextStep(
      65             :     GDALPipelineStepAlgorithm *poNextStep) const
      66             : {
      67           5 :     return poNextStep->GetName() == GDALVectorWriteAlgorithm::NAME &&
      68           5 :            poNextStep->GetOutputFormat() != "stream";
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                GDALRasterPolygonizeAlgorithm::RunImpl()              */
      73             : /************************************************************************/
      74             : 
      75          18 : bool GDALRasterPolygonizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
      76             :                                             void *pProgressData)
      77             : {
      78          18 :     GDALPipelineStepRunContext stepCtxt;
      79          18 :     stepCtxt.m_pfnProgress = pfnProgress;
      80          18 :     stepCtxt.m_pProgressData = pProgressData;
      81          18 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                GDALRasterPolygonizeAlgorithm::RunStep()              */
      86             : /************************************************************************/
      87             : 
      88          21 : bool GDALRasterPolygonizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
      89             : {
      90          21 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      91          21 :     CPLAssert(poSrcDS);
      92             : 
      93          21 :     auto poWriteStep = ctxt.m_poNextUsableStep ? ctxt.m_poNextUsableStep : this;
      94             : 
      95          21 :     GDALDataset *poDstDS = poWriteStep->GetOutputDataset().GetDatasetRef();
      96          21 :     std::unique_ptr<GDALDataset> poRetDS;
      97          42 :     std::string outputFilename = poWriteStep->GetOutputDataset().GetName();
      98          21 :     GDALDriver *poDstDriver = nullptr;
      99          21 :     bool bTemporaryFile = false;
     100          21 :     if (!poDstDS)
     101             :     {
     102          16 :         auto poDriverManager = GetGDALDriverManager();
     103          16 :         std::string format = poWriteStep->GetOutputFormat();
     104          16 :         if (m_standaloneStep || (ctxt.m_poNextUsableStep && format.empty()))
     105             :         {
     106          14 :             if (format.empty())
     107             :             {
     108             :                 const auto aosFormats =
     109             :                     CPLStringList(GDALGetOutputDriversForDatasetName(
     110             :                         outputFilename.c_str(), GDAL_OF_VECTOR,
     111             :                         /* bSingleMatch = */ true,
     112           9 :                         /* bWarn = */ true));
     113           9 :                 if (aosFormats.size() != 1)
     114             :                 {
     115           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     116             :                                 "Cannot guess driver for %s",
     117             :                                 outputFilename.c_str());
     118           1 :                     return false;
     119             :                 }
     120           8 :                 format = aosFormats[0];
     121             :             }
     122             :         }
     123           2 :         else if (!ctxt.m_poNextUsableStep)
     124             :         {
     125           1 :             poDstDriver = poDriverManager->GetDriverByName("GPKG");
     126           1 :             if (poDstDriver)
     127             :             {
     128           1 :                 bTemporaryFile = true;
     129             :                 outputFilename =
     130           1 :                     CPLGenerateTempFilenameSafe("_polygonize") + ".gpkg";
     131           1 :                 format = "GPKG";
     132             :             }
     133             :             else
     134           0 :                 format = "MEM";
     135             :         }
     136             : 
     137          15 :         if (!poDstDriver)
     138          14 :             poDstDriver = poDriverManager->GetDriverByName(format.c_str());
     139          15 :         if (!poDstDriver)
     140             :         {
     141           0 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     142             :                         format.c_str());
     143           0 :             return false;
     144             :         }
     145             : 
     146          15 :         poRetDS.reset(poDstDriver->Create(
     147             :             outputFilename.c_str(), 0, 0, 0, GDT_Unknown,
     148          30 :             CPLStringList(poWriteStep->GetCreationOptions()).List()));
     149          15 :         if (!poRetDS)
     150           1 :             return false;
     151             : 
     152          14 :         if (bTemporaryFile)
     153           1 :             poRetDS->MarkSuppressOnClose();
     154             : 
     155          14 :         poDstDS = poRetDS.get();
     156             :     }
     157             :     else
     158             :     {
     159           5 :         poDstDriver = poDstDS->GetDriver();
     160             :     }
     161             : 
     162          38 :     std::string outputLayerName = poWriteStep->GetOutputLayerName();
     163          19 :     if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
     164          43 :         EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") &&
     165           5 :         poDstDS->GetLayerCount() <= 1)
     166             :     {
     167           5 :         outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
     168             :     }
     169             : 
     170          19 :     auto poDstLayer = poDstDS->GetLayerByName(outputLayerName.c_str());
     171          19 :     if (poDstLayer)
     172             :     {
     173           4 :         if (poWriteStep->GetOverwriteLayer())
     174             :         {
     175           1 :             int iLayer = -1;
     176           1 :             const int nLayerCount = poDstDS->GetLayerCount();
     177           1 :             for (iLayer = 0; iLayer < nLayerCount; iLayer++)
     178             :             {
     179           1 :                 if (poDstDS->GetLayer(iLayer) == poDstLayer)
     180           1 :                     break;
     181             :             }
     182             : 
     183           1 :             if (iLayer < nLayerCount)
     184             :             {
     185           1 :                 if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
     186             :                 {
     187           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     188             :                                 "Cannot delete layer '%s'",
     189             :                                 outputLayerName.c_str());
     190           0 :                     return false;
     191             :                 }
     192             :             }
     193           1 :             poDstLayer = nullptr;
     194             :         }
     195           3 :         else if (!poWriteStep->GetAppendLayer())
     196             :         {
     197           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     198             :                         "Layer '%s' already exists. Specify the "
     199             :                         "--%s option to overwrite it, or --%s "
     200             :                         "to append to it.",
     201             :                         outputLayerName.c_str(), GDAL_ARG_NAME_OVERWRITE_LAYER,
     202             :                         GDAL_ARG_NAME_APPEND);
     203           1 :             return false;
     204             :         }
     205             :     }
     206          15 :     else if (poWriteStep->GetAppendLayer() || poWriteStep->GetOverwriteLayer())
     207             :     {
     208           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
     209             :                     outputLayerName.c_str());
     210           1 :         return false;
     211             :     }
     212             : 
     213          17 :     auto poSrcBand = poSrcDS->GetRasterBand(m_band);
     214          17 :     const auto eDT = poSrcBand->GetRasterDataType();
     215             : 
     216          17 :     if (!poDstLayer)
     217             :     {
     218          15 :         poDstLayer = poDstDS->CreateLayer(
     219          15 :             outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
     220          30 :             CPLStringList(poWriteStep->GetLayerCreationOptions()).List());
     221          15 :         if (!poDstLayer)
     222             :         {
     223           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
     224             :                         outputLayerName.c_str());
     225           1 :             return false;
     226             :         }
     227             : 
     228             :         OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
     229          14 :                                 !GDALDataTypeIsInteger(eDT) ? OFTReal
     230          12 :                                 : eDT == GDT_Int64 || eDT == GDT_UInt64
     231          25 :                                     ? OFTInteger64
     232          27 :                                     : OFTInteger);
     233          14 :         if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
     234             :         {
     235           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     236             :                         "Cannot create field '%s' in layer '%s'",
     237             :                         m_attributeName.c_str(), outputLayerName.c_str());
     238           0 :             return false;
     239             :         }
     240             :     }
     241             : 
     242             :     const int iPixValField =
     243          16 :         poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
     244          16 :     if (iPixValField < 0)
     245             :     {
     246           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     247             :                     "Cannot find field '%s' in layer '%s'",
     248             :                     m_attributeName.c_str(), outputLayerName.c_str());
     249           1 :         return false;
     250             :     }
     251             : 
     252          15 :     CPLStringList aosPolygonizeOptions;
     253          15 :     if (m_connectDiagonalPixels)
     254             :     {
     255           1 :         aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
     256             :     }
     257          15 :     if (m_commitInterval)
     258             :     {
     259             :         aosPolygonizeOptions.SetNameValue("COMMIT_INTERVAL",
     260           2 :                                           CPLSPrintf("%d", m_commitInterval));
     261             :     }
     262             : 
     263             :     bool ret;
     264          15 :     if (GDALDataTypeIsInteger(eDT))
     265             :     {
     266          28 :         ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     267          14 :                              GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     268             :                              OGRLayer::ToHandle(poDstLayer), iPixValField,
     269             :                              aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     270             :                              ctxt.m_pProgressData) == CE_None;
     271             :     }
     272             :     else
     273             :     {
     274           1 :         ret =
     275           2 :             GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     276           1 :                             GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     277             :                             OGRLayer::ToHandle(poDstLayer), iPixValField,
     278             :                             aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     279             :                             ctxt.m_pProgressData) == CE_None;
     280             :     }
     281             : 
     282          15 :     if (ret && poRetDS)
     283             :     {
     284          13 :         if (bTemporaryFile)
     285             :         {
     286           1 :             ret = poRetDS->FlushCache() == CE_None;
     287           1 :             VSIUnlink(outputFilename.c_str());
     288             :         }
     289             : 
     290          13 :         m_outputDataset.Set(std::move(poRetDS));
     291             :     }
     292             : 
     293          15 :     return ret;
     294             : }
     295             : 
     296             : GDALRasterPolygonizeAlgorithmStandalone::
     297             :     ~GDALRasterPolygonizeAlgorithmStandalone() = default;
     298             : 
     299             : //! @endcond

Generated by: LCOV version 1.14