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

Generated by: LCOV version 1.14