LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_polygonize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 126 134 94.0 %
Date: 2026-06-03 12:46:18 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 = poWriteStep->GetOutputDataset().GetDatasetRef();
     101          28 :     std::unique_ptr<GDALDataset> poRetDS;
     102          56 :     std::string outputFilename = poWriteStep->GetOutputDataset().GetName();
     103          28 :     GDALDriver *poDstDriver = nullptr;
     104          28 :     bool bTemporaryFile = false;
     105          28 :     if (!poDstDS)
     106             :     {
     107          23 :         auto poDriverManager = GetGDALDriverManager();
     108          23 :         std::string format = poWriteStep->GetOutputFormat();
     109          23 :         if (m_standaloneStep || (ctxt.m_poNextUsableStep && format.empty()))
     110             :         {
     111          18 :             if (format.empty())
     112             :             {
     113             :                 const auto aosFormats =
     114             :                     CPLStringList(GDALGetOutputDriversForDatasetName(
     115             :                         outputFilename.c_str(), GDAL_OF_VECTOR,
     116             :                         /* bSingleMatch = */ true,
     117          13 :                         /* bWarn = */ true));
     118          13 :                 if (aosFormats.size() != 1)
     119             :                 {
     120           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     121             :                                 "Cannot guess driver for %s",
     122             :                                 outputFilename.c_str());
     123           1 :                     return false;
     124             :                 }
     125          12 :                 format = aosFormats[0];
     126             :             }
     127             :         }
     128           5 :         else if (!ctxt.m_poNextUsableStep)
     129             :         {
     130           3 :             poDstDriver = poDriverManager->GetDriverByName("GPKG");
     131           3 :             if (poDstDriver)
     132             :             {
     133           3 :                 bTemporaryFile = true;
     134             :                 outputFilename =
     135           3 :                     CPLGenerateTempFilenameSafe("_polygonize") + ".gpkg";
     136           3 :                 format = "GPKG";
     137             :             }
     138             :             else
     139           0 :                 format = "MEM";
     140             :         }
     141             : 
     142          22 :         if (!poDstDriver)
     143          19 :             poDstDriver = poDriverManager->GetDriverByName(format.c_str());
     144          22 :         if (!poDstDriver)
     145             :         {
     146           0 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     147             :                         format.c_str());
     148           0 :             return false;
     149             :         }
     150             : 
     151          22 :         poRetDS.reset(poDstDriver->Create(
     152             :             outputFilename.c_str(), 0, 0, 0, GDT_Unknown,
     153          44 :             CPLStringList(poWriteStep->GetCreationOptions()).List()));
     154          22 :         if (!poRetDS)
     155           1 :             return false;
     156             : 
     157          21 :         if (bTemporaryFile)
     158           3 :             poRetDS->MarkSuppressOnClose();
     159             : 
     160          21 :         poDstDS = poRetDS.get();
     161             :     }
     162             :     else
     163             :     {
     164           5 :         poDstDriver = poDstDS->GetDriver();
     165             :     }
     166             : 
     167          52 :     std::string outputLayerName = poWriteStep->GetOutputLayerName();
     168          26 :     if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
     169          35 :         (EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") ||
     170          26 :          EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(),
     171          52 :                "shz")) &&
     172           9 :         poDstDS->GetLayerCount() <= 1)
     173             :     {
     174           9 :         outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
     175             :     }
     176          26 :     if (outputLayerName.empty())
     177          15 :         outputLayerName = "polygonize";
     178             : 
     179          26 :     auto poDstLayer = poDstDS->GetLayerByName(outputLayerName.c_str());
     180          26 :     if (poDstLayer)
     181             :     {
     182           4 :         if (poWriteStep->GetOverwriteLayer())
     183             :         {
     184           1 :             int iLayer = -1;
     185           1 :             const int nLayerCount = poDstDS->GetLayerCount();
     186           1 :             for (iLayer = 0; iLayer < nLayerCount; iLayer++)
     187             :             {
     188           1 :                 if (poDstDS->GetLayer(iLayer) == poDstLayer)
     189           1 :                     break;
     190             :             }
     191             : 
     192           1 :             if (iLayer < nLayerCount)
     193             :             {
     194           1 :                 if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
     195             :                 {
     196           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     197             :                                 "Cannot delete layer '%s'",
     198             :                                 outputLayerName.c_str());
     199           0 :                     return false;
     200             :                 }
     201             :             }
     202           1 :             poDstLayer = nullptr;
     203             :         }
     204           3 :         else if (!poWriteStep->GetAppendLayer())
     205             :         {
     206           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     207             :                         "Layer '%s' already exists. Specify the "
     208             :                         "--%s option to overwrite it, or --%s "
     209             :                         "to append to it.",
     210             :                         outputLayerName.c_str(), GDAL_ARG_NAME_OVERWRITE_LAYER,
     211             :                         GDAL_ARG_NAME_APPEND);
     212           1 :             return false;
     213             :         }
     214             :     }
     215          22 :     else if (poWriteStep->GetAppendLayer() || poWriteStep->GetOverwriteLayer())
     216             :     {
     217           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
     218             :                     outputLayerName.c_str());
     219           1 :         return false;
     220             :     }
     221             : 
     222          24 :     auto poSrcBand = poSrcDS->GetRasterBand(m_band);
     223          24 :     const auto eDT = poSrcBand->GetRasterDataType();
     224             : 
     225          24 :     if (!poDstLayer)
     226             :     {
     227          22 :         poDstLayer = poDstDS->CreateLayer(
     228          22 :             outputLayerName.c_str(), poSrcDS->GetSpatialRef(), wkbPolygon,
     229          44 :             CPLStringList(poWriteStep->GetLayerCreationOptions()).List());
     230          22 :         if (!poDstLayer)
     231             :         {
     232           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot create layer '%s'",
     233             :                         outputLayerName.c_str());
     234           1 :             return false;
     235             :         }
     236             : 
     237             :         OGRFieldDefn oFieldDefn(m_attributeName.c_str(),
     238          21 :                                 !GDALDataTypeIsInteger(eDT) ? OFTReal
     239          19 :                                 : eDT == GDT_Int64 || eDT == GDT_UInt64
     240          39 :                                     ? OFTInteger64
     241          41 :                                     : OFTInteger);
     242          21 :         if (poDstLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
     243             :         {
     244           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     245             :                         "Cannot create field '%s' in layer '%s'",
     246             :                         m_attributeName.c_str(), outputLayerName.c_str());
     247           0 :             return false;
     248             :         }
     249             :     }
     250             : 
     251             :     const int iPixValField =
     252          23 :         poDstLayer->GetLayerDefn()->GetFieldIndex(m_attributeName.c_str());
     253          23 :     if (iPixValField < 0)
     254             :     {
     255           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     256             :                     "Cannot find field '%s' in layer '%s'",
     257             :                     m_attributeName.c_str(), outputLayerName.c_str());
     258           1 :         return false;
     259             :     }
     260             : 
     261          22 :     CPLStringList aosPolygonizeOptions;
     262          22 :     if (m_connectDiagonalPixels)
     263             :     {
     264           1 :         aosPolygonizeOptions.SetNameValue("8CONNECTED", "8");
     265             :     }
     266          22 :     if (m_commitInterval)
     267             :     {
     268             :         aosPolygonizeOptions.SetNameValue("COMMIT_INTERVAL",
     269           2 :                                           CPLSPrintf("%d", m_commitInterval));
     270             :     }
     271             : 
     272             :     bool ret;
     273          22 :     if (GDALDataTypeIsInteger(eDT))
     274             :     {
     275          42 :         ret = GDALPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     276          21 :                              GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     277             :                              OGRLayer::ToHandle(poDstLayer), iPixValField,
     278             :                              aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     279             :                              ctxt.m_pProgressData) == CE_None;
     280             :     }
     281             :     else
     282             :     {
     283           1 :         ret =
     284           2 :             GDALFPolygonize(GDALRasterBand::ToHandle(poSrcBand),
     285           1 :                             GDALRasterBand::ToHandle(poSrcBand->GetMaskBand()),
     286             :                             OGRLayer::ToHandle(poDstLayer), iPixValField,
     287             :                             aosPolygonizeOptions.List(), ctxt.m_pfnProgress,
     288             :                             ctxt.m_pProgressData) == CE_None;
     289             :     }
     290             : 
     291          22 :     if (ret && poRetDS)
     292             :     {
     293          20 :         if (bTemporaryFile)
     294             :         {
     295           3 :             ret = poRetDS->FlushCache() == CE_None;
     296             : #if !defined(__APPLE__)
     297             :             // For some unknown reason, unlinking the file on MacOSX
     298             :             // leads to later "disk I/O error". See https://github.com/OSGeo/gdal/issues/13794
     299           3 :             VSIUnlink(outputFilename.c_str());
     300             : #endif
     301             :         }
     302             : 
     303          20 :         m_outputDataset.Set(std::move(poRetDS));
     304             :     }
     305             : 
     306          22 :     return ret;
     307             : }
     308             : 
     309             : GDALRasterPolygonizeAlgorithmStandalone::
     310             :     ~GDALRasterPolygonizeAlgorithmStandalone() = default;
     311             : 
     312             : //! @endcond

Generated by: LCOV version 1.14