LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_rasterize.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 220 230 95.7 %
Date: 2026-07-24 18:27:47 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "vector rasterize" subcommand
       5             :  * Author:   Alessandro Pasotti <elpaso at itopen dot it>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Alessandro Pasotti <elpaso at itopen dot it>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include <cmath>
      14             : 
      15             : #include "gdalalg_vector_rasterize.h"
      16             : #include "gdalalg_raster_write.h"
      17             : 
      18             : #include "cpl_conv.h"
      19             : #include "gdal_priv.h"
      20             : #include "gdal_utils.h"
      21             : 
      22             : //! @cond Doxygen_Suppress
      23             : 
      24             : #ifndef _
      25             : #define _(x) (x)
      26             : #endif
      27             : 
      28             : /************************************************************************/
      29             : /*     GDALVectorRasterizeAlgorithm::GDALVectorRasterizeAlgorithm()     */
      30             : /************************************************************************/
      31             : 
      32         123 : GDALVectorRasterizeAlgorithm::GDALVectorRasterizeAlgorithm(bool bStandaloneStep)
      33             :     : GDALPipelineStepAlgorithm(
      34             :           NAME, DESCRIPTION, HELP_URL,
      35           0 :           ConstructorOptions()
      36         123 :               .SetStandaloneStep(bStandaloneStep)
      37         246 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      38             : {
      39         123 :     if (bStandaloneStep)
      40             :     {
      41         104 :         AddProgressArg();
      42         104 :         AddOutputFormatArg(&m_format)
      43             :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      44         416 :                              {GDAL_DCAP_RASTER, GDAL_DCAP_CREATE})
      45         208 :             .AddMetadataItem(GAAMDI_VRT_COMPATIBLE, {"false"});
      46         104 :         AddOpenOptionsArg(&m_openOptions);
      47         104 :         AddInputFormatsArg(&m_inputFormats)
      48         208 :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
      49         104 :         AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR)
      50         104 :             .SetMinCount(1)
      51         104 :             .SetMaxCount(1);
      52         104 :         AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER)
      53         104 :             .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      54         104 :         AddCreationOptionsArg(&m_creationOptions);
      55         104 :         AddOverwriteArg(&m_overwrite);
      56             :     }
      57             :     else
      58             :     {
      59          19 :         AddVectorHiddenInputDatasetArg();
      60             :     }
      61             : 
      62         123 :     AddBandArg(&m_bands, _("The band(s) to burn values into (1-based index)"));
      63         246 :     AddArg("invert", 0, _("Invert the rasterization"), &m_invert)
      64         123 :         .SetDefault(false);
      65             :     AddArg("all-touched", 0, _("Enables the ALL_TOUCHED rasterization option"),
      66         123 :            &m_allTouched);
      67         123 :     AddArg("burn", 0, _("Burn value"), &m_burnValues);
      68         123 :     AddArg("attribute-name", 'a', _("Attribute name"), &m_attributeName);
      69             :     AddArg("3d", 0,
      70             :            _("Indicates that a burn value should be extracted from the Z"
      71             :              " values of the feature"),
      72         123 :            &m_3d);
      73         123 :     AddLayerNameArg(&m_layerName).SetMutualExclusionGroup("layer-name-or-sql");
      74         123 :     AddArg("where", 0, _("SQL where clause"), &m_where);
      75         246 :     AddArg("sql", 0, _("SQL select statement"), &m_sql)
      76         123 :         .SetMutualExclusionGroup("layer-name-or-sql");
      77         123 :     AddArg("dialect", 0, _("SQL dialect"), &m_dialect);
      78             :     AddArg("nodata", 0, _("Assign a specified nodata value to output bands"),
      79         123 :            &m_nodata);
      80             :     AddArg("init", 0, _("Pre-initialize output bands with specified value"),
      81         123 :            &m_initValues);
      82             :     AddArg("like", 0,
      83             :            _("Dataset to use as a template for bounds, CRS and resolution"),
      84         246 :            &m_likeDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR)
      85         123 :         .SetMetaVar("DATASET");
      86         246 :     AddArg("crs", 0, _("Override the projection for the output file"), &m_srs)
      87         246 :         .AddHiddenAlias("srs")
      88         123 :         .SetIsCRSArg(/*noneAllowed=*/false);
      89             :     AddArg("transformer-option", 0,
      90             :            _("Set a transformer option suitable to pass to "
      91             :              "GDALCreateGenImgProjTransformer2"),
      92         246 :            &m_transformerOption)
      93         123 :         .SetMetaVar("<NAME>=<VALUE>");
      94             :     AddArg("extent", 0, _("Set the target georeferenced extent"),
      95         246 :            &m_targetExtent)
      96         123 :         .SetMinCount(4)
      97         123 :         .SetMaxCount(4)
      98         123 :         .SetRepeatedArgAllowed(false)
      99         123 :         .SetMetaVar("<xmin>,<ymin>,<xmax>,<ymax>");
     100             :     auto &argResolution =
     101             :         AddArg("resolution", 0, _("Set the target resolution"),
     102         246 :                &m_targetResolution)
     103         123 :             .SetMinCount(2)
     104         123 :             .SetMaxCount(2)
     105         123 :             .SetRepeatedArgAllowed(false)
     106         246 :             .SetMetaVar("<xres>,<yres>")
     107         123 :             .SetMutualExclusionGroup("size-or-resolution");
     108             :     AddArg("target-aligned-pixels", 0,
     109             :            _("(target aligned pixels) Align the coordinates of the extent of "
     110             :              "the output file to the values of the resolution"),
     111         246 :            &m_tap)
     112         246 :         .AddAlias("tap")
     113         123 :         .AddDirectDependency(argResolution);
     114             :     AddArg("size", 0, _("Set the target size in pixels and lines"),
     115         246 :            &m_targetSize)
     116         123 :         .SetMinCount(2)
     117         123 :         .SetMaxCount(2)
     118         123 :         .SetRepeatedArgAllowed(false)
     119         246 :         .SetMetaVar("<xsize>,<ysize>")
     120         123 :         .SetMutualExclusionGroup("size-or-resolution");
     121         123 :     AddOutputDataTypeArg(&m_outputType);
     122             :     AddArg("optimization", 0,
     123             :            _("Force the algorithm used (results are identical)"),
     124         246 :            &m_optimization)
     125         123 :         .SetChoices("AUTO", "RASTER", "VECTOR")
     126         123 :         .SetDefault("AUTO");
     127             : 
     128         123 :     if (bStandaloneStep)
     129             :     {
     130         208 :         auto &addArg = AddArg("add", 0, _("Add to existing raster"), &m_add)
     131         104 :                            .SetDefault(false);
     132         104 :         auto &updateArg = AddUpdateArg(&m_update);
     133             :         addArg.AddValidationAction(
     134           3 :             [&updateArg]()
     135             :             {
     136           3 :                 updateArg.Set(true);
     137           3 :                 return true;
     138         104 :             });
     139             :     }
     140             : 
     141         123 :     AddValidationAction(
     142          99 :         [this]()
     143             :         {
     144          97 :             if (m_likeDataset.GetDatasetRef() &&
     145           2 :                 (!m_targetExtent.empty() || !m_srs.empty()))
     146             :             {
     147           1 :                 ReportError(
     148             :                     CE_Failure, CPLE_AppDefined,
     149             :                     "--like is mutually exclusive with --extent and --crs");
     150           1 :                 return false;
     151             :             }
     152          94 :             return true;
     153             :         });
     154         123 : }
     155             : 
     156             : /************************************************************************/
     157             : /*               GDALVectorRasterizeAlgorithm::RunStep()                */
     158             : /************************************************************************/
     159             : 
     160          55 : bool GDALVectorRasterizeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     161             : {
     162          55 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     163          55 :     CPLAssert(poSrcDS);
     164             : 
     165         110 :     CPLStringList aosOptions;
     166             : 
     167          55 :     if (m_bands.size())
     168             :     {
     169          72 :         for (int band : m_bands)
     170             :         {
     171          54 :             aosOptions.AddString("-b");
     172          54 :             aosOptions.AddString(CPLSPrintf("%d", band));
     173             :         }
     174             :     }
     175             : 
     176          55 :     if (m_invert)
     177             :     {
     178           1 :         aosOptions.AddString("-i");
     179             :     }
     180             : 
     181          55 :     if (m_allTouched)
     182             :     {
     183          31 :         aosOptions.AddString("-at");
     184             :     }
     185             : 
     186          55 :     if (m_burnValues.size())
     187             :     {
     188         150 :         for (double burnValue : m_burnValues)
     189             :         {
     190         104 :             aosOptions.AddString("-burn");
     191         104 :             aosOptions.AddString(CPLSPrintf("%.17g", burnValue));
     192             :         }
     193             :     }
     194             : 
     195          55 :     if (!m_attributeName.empty())
     196             :     {
     197           5 :         aosOptions.AddString("-a");
     198           5 :         aosOptions.AddString(m_attributeName.c_str());
     199             :     }
     200             : 
     201          55 :     if (m_3d)
     202             :     {
     203           2 :         aosOptions.AddString("-3d");
     204             :     }
     205             : 
     206          55 :     if (m_add)
     207             :     {
     208           1 :         aosOptions.AddString("-add");
     209             :         // Implies update
     210           1 :         m_update = true;
     211             :     }
     212             : 
     213          55 :     if (!m_layerName.empty())
     214             :     {
     215          22 :         aosOptions.AddString("-l");
     216          22 :         aosOptions.AddString(m_layerName.c_str());
     217             :     }
     218             : 
     219          55 :     if (!m_where.empty())
     220             :     {
     221           1 :         aosOptions.AddString("-where");
     222           1 :         aosOptions.AddString(m_where.c_str());
     223             :     }
     224             : 
     225          55 :     if (!m_sql.empty())
     226             :     {
     227           7 :         aosOptions.AddString("-sql");
     228           7 :         aosOptions.AddString(m_sql.c_str());
     229             :     }
     230             : 
     231          55 :     if (!m_dialect.empty())
     232             :     {
     233           3 :         aosOptions.AddString("-dialect");
     234           3 :         aosOptions.AddString(m_dialect.c_str());
     235             :     }
     236             : 
     237         110 :     std::string outputFilename;
     238          55 :     if (m_standaloneStep)
     239             :     {
     240          53 :         outputFilename = m_outputDataset.GetName();
     241          53 :         if (!m_format.empty())
     242             :         {
     243           5 :             aosOptions.AddString("-of");
     244           5 :             aosOptions.AddString(m_format.c_str());
     245             :         }
     246             : 
     247          54 :         for (const std::string &co : m_creationOptions)
     248             :         {
     249           1 :             aosOptions.AddString("-co");
     250           1 :             aosOptions.AddString(co.c_str());
     251             :         }
     252             :     }
     253             :     else
     254             :     {
     255           2 :         outputFilename = CPLGenerateTempFilenameSafe("_rasterize.tif");
     256             : 
     257           2 :         aosOptions.AddString("-of");
     258           2 :         aosOptions.AddString("GTiff");
     259             : 
     260           2 :         aosOptions.AddString("-co");
     261           2 :         aosOptions.AddString("TILED=YES");
     262             :     }
     263             : 
     264          55 :     if (!std::isnan(m_nodata))
     265             :     {
     266           2 :         if (m_update)
     267             :         {
     268           1 :             ReportError(
     269             :                 CE_Failure, CPLE_AppDefined,
     270             :                 "Cannot specify --nodata when updating an existing raster.");
     271           1 :             return false;
     272             :         }
     273           1 :         aosOptions.AddString("-a_nodata");
     274           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_nodata));
     275             :     }
     276             : 
     277          54 :     if (m_initValues.size())
     278             :     {
     279          36 :         for (double initValue : m_initValues)
     280             :         {
     281          27 :             aosOptions.AddString("-init");
     282          27 :             aosOptions.AddString(CPLSPrintf("%.17g", initValue));
     283             :         }
     284             :     }
     285             : 
     286          54 :     if (auto *poLikeDS = m_likeDataset.GetDatasetRef())
     287             :     {
     288           1 :         OGREnvelope sExtent;
     289           1 :         if (poLikeDS->GetExtent(&sExtent) != CE_None)
     290             :         {
     291           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     292             :                         "Cannot get extent of '%s'",
     293           0 :                         poLikeDS->GetDescription());
     294           0 :             return false;
     295             :         }
     296           1 :         aosOptions.AddString("-te");
     297           1 :         aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MinX));
     298           1 :         aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MinY));
     299           1 :         aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MaxX));
     300           1 :         aosOptions.AddString(CPLSPrintf("%.17g", sExtent.MaxY));
     301             : 
     302           1 :         const auto poSRS = poLikeDS->GetSpatialRef();
     303           1 :         if (poSRS)
     304             :         {
     305           1 :             const char *const options[] = {"FORMAT=WKT2_2019", nullptr};
     306           2 :             const std::string osCRS = poSRS->exportToWkt(options);
     307           1 :             aosOptions.AddString("-a_srs");
     308           1 :             aosOptions.AddString(osCRS.c_str());
     309             :         }
     310             : 
     311           1 :         if (m_targetResolution.empty() && m_targetSize.empty())
     312             :         {
     313           1 :             GDALGeoTransform gt;
     314           1 :             if (poLikeDS->GetGeoTransform(gt) == CE_None)
     315             :             {
     316           1 :                 if (gt.xrot != 0 || gt.yrot != 0)
     317             :                 {
     318           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
     319             :                                 "Geotransform matrix of '%s' has non-zero "
     320             :                                 "rotation terms",
     321           0 :                                 poLikeDS->GetDescription());
     322           0 :                     return false;
     323             :                 }
     324           1 :                 aosOptions.AddString("-ts");
     325             :                 aosOptions.AddString(
     326           1 :                     CPLSPrintf("%d", poLikeDS->GetRasterXSize()));
     327             :                 aosOptions.AddString(
     328           1 :                     CPLSPrintf("%d", poLikeDS->GetRasterYSize()));
     329             :             }
     330             :         }
     331             :     }
     332             :     else
     333             :     {
     334          53 :         if (m_targetExtent.size())
     335             :         {
     336           1 :             aosOptions.AddString("-te");
     337           5 :             for (double targetExtent : m_targetExtent)
     338             :             {
     339           4 :                 aosOptions.AddString(CPLSPrintf("%.17g", targetExtent));
     340             :             }
     341             :         }
     342             : 
     343          53 :         if (!m_srs.empty())
     344             :         {
     345           2 :             if (m_update)
     346             :             {
     347           1 :                 ReportError(
     348             :                     CE_Failure, CPLE_AppDefined,
     349             :                     "Cannot specify --crs when updating an existing raster.");
     350           1 :                 return false;
     351             :             }
     352           1 :             aosOptions.AddString("-a_srs");
     353           1 :             aosOptions.AddString(m_srs.c_str());
     354             :         }
     355             :     }
     356             : 
     357          53 :     if (m_transformerOption.size())
     358             :     {
     359           0 :         for (const auto &to : m_transformerOption)
     360             :         {
     361           0 :             aosOptions.AddString("-to");
     362           0 :             aosOptions.AddString(to.c_str());
     363             :         }
     364             :     }
     365             : 
     366          53 :     if (m_tap)
     367             :     {
     368           1 :         aosOptions.AddString("-tap");
     369             :     }
     370             : 
     371          53 :     if (m_targetResolution.size())
     372             :     {
     373           7 :         if (m_update)
     374             :         {
     375           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     376             :                         "Cannot specify --resolution when updating an existing "
     377             :                         "raster.");
     378           1 :             return false;
     379             :         }
     380           6 :         aosOptions.AddString("-tr");
     381          18 :         for (double targetResolution : m_targetResolution)
     382             :         {
     383          12 :             aosOptions.AddString(CPLSPrintf("%.17g", targetResolution));
     384             :         }
     385             :     }
     386          46 :     else if (m_targetSize.size())
     387             :     {
     388          26 :         if (m_update)
     389             :         {
     390           1 :             ReportError(
     391             :                 CE_Failure, CPLE_AppDefined,
     392             :                 "Cannot specify --size when updating an existing raster.");
     393           1 :             return false;
     394             :         }
     395          25 :         aosOptions.AddString("-ts");
     396          75 :         for (int targetSize : m_targetSize)
     397             :         {
     398          50 :             aosOptions.AddString(CPLSPrintf("%d", targetSize));
     399             :         }
     400             :     }
     401          20 :     else if (!m_likeDataset.GetDatasetRef() && !m_outputDataset.GetDatasetRef())
     402             :     {
     403           3 :         ReportError(
     404             :             CE_Failure, CPLE_AppDefined,
     405             :             "Must specify output resolution (--resolution) or size (--size) "
     406             :             "when writing rasterized features to a new dataset.");
     407           3 :         return false;
     408             :     }
     409             : 
     410          48 :     if (!m_outputType.empty())
     411             :     {
     412           2 :         if (m_update)
     413             :         {
     414           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     415             :                         "Cannot specify --output-data-type when updating an "
     416             :                         "existing raster.");
     417           1 :             return false;
     418             :         }
     419           1 :         aosOptions.AddString("-ot");
     420           1 :         aosOptions.AddString(m_outputType.c_str());
     421             :     }
     422             : 
     423          47 :     if (!m_optimization.empty())
     424             :     {
     425          47 :         aosOptions.AddString("-optim");
     426          47 :         aosOptions.AddString(m_optimization.c_str());
     427             :     }
     428             : 
     429          47 :     bool bOK = false;
     430             :     std::unique_ptr<GDALRasterizeOptions, decltype(&GDALRasterizeOptionsFree)>
     431             :         psOptions{GDALRasterizeOptionsNew(aosOptions.List(), nullptr),
     432          47 :                   GDALRasterizeOptionsFree};
     433          47 :     if (psOptions)
     434             :     {
     435          44 :         GDALRasterizeOptionsSetProgress(psOptions.get(), ctxt.m_pfnProgress,
     436             :                                         ctxt.m_pProgressData);
     437             : 
     438             :         GDALDatasetH hDstDS =
     439          44 :             GDALDataset::ToHandle(m_outputDataset.GetDatasetRef());
     440             : 
     441          44 :         GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
     442             : 
     443          44 :         auto poRetDS = GDALDataset::FromHandle(GDALRasterize(
     444          44 :             outputFilename.c_str(), hDstDS, hSrcDS, psOptions.get(), nullptr));
     445          44 :         bOK = poRetDS != nullptr;
     446             : 
     447          44 :         if (!hDstDS)
     448             :         {
     449          31 :             if (!m_standaloneStep && poRetDS)
     450             :             {
     451           2 :                 VSIUnlink(outputFilename.c_str());
     452           2 :                 poRetDS->MarkSuppressOnClose();
     453             :             }
     454             : 
     455          31 :             m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
     456             :         }
     457             :     }
     458             : 
     459          47 :     return bOK;
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*               GDALVectorRasterizeAlgorithm::RunImpl()                */
     464             : /************************************************************************/
     465             : 
     466          53 : bool GDALVectorRasterizeAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     467             :                                            void *pProgressData)
     468             : {
     469          53 :     GDALPipelineStepRunContext stepCtxt;
     470          53 :     stepCtxt.m_pfnProgress = pfnProgress;
     471          53 :     stepCtxt.m_pProgressData = pProgressData;
     472          53 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
     473             : }
     474             : 
     475             : GDALVectorRasterizeAlgorithmStandalone::
     476             :     ~GDALVectorRasterizeAlgorithmStandalone() = default;
     477             : 
     478             : //! @endcond

Generated by: LCOV version 1.14