LCOV - code coverage report
Current view: top level - apps - gdal_rasterize_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 553 695 79.6 %
Date: 2025-01-18 12:42:00 Functions: 18 24 75.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  Rasterize OGR shapes into a GDAL raster.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2015, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_utils.h"
      16             : #include "gdal_utils_priv.h"
      17             : 
      18             : #include <cmath>
      19             : #include <cstdio>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <algorithm>
      23             : #include <limits>
      24             : #include <vector>
      25             : 
      26             : #include "commonutils.h"
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_progress.h"
      30             : #include "cpl_string.h"
      31             : #include "gdal.h"
      32             : #include "gdal_alg.h"
      33             : #include "gdal_priv.h"
      34             : #include "ogr_api.h"
      35             : #include "ogr_core.h"
      36             : #include "ogr_srs_api.h"
      37             : #include "gdalargumentparser.h"
      38             : 
      39             : /************************************************************************/
      40             : /*                          GDALRasterizeOptions()                      */
      41             : /************************************************************************/
      42             : 
      43             : struct GDALRasterizeOptions
      44             : {
      45             :     std::vector<int> anBandList{};
      46             :     std::vector<double> adfBurnValues{};
      47             :     bool bInverse = false;
      48             :     std::string osFormat{};
      49             :     bool b3D = false;
      50             :     GDALProgressFunc pfnProgress = GDALDummyProgress;
      51             :     void *pProgressData = nullptr;
      52             :     std::vector<std::string> aosLayers{};
      53             :     std::string osSQL{};
      54             :     std::string osDialect{};
      55             :     std::string osBurnAttribute{};
      56             :     std::string osWHERE{};
      57             :     CPLStringList aosRasterizeOptions{};
      58             :     CPLStringList aosTO{};
      59             :     double dfXRes = 0;
      60             :     double dfYRes = 0;
      61             :     CPLStringList aosCreationOptions{};
      62             :     GDALDataType eOutputType = GDT_Unknown;
      63             :     std::vector<double> adfInitVals{};
      64             :     std::string osNoData{};
      65             :     OGREnvelope sEnvelop{};
      66             :     int nXSize = 0;
      67             :     int nYSize = 0;
      68             :     OGRSpatialReference oOutputSRS{};
      69             : 
      70             :     bool bTargetAlignedPixels = false;
      71             :     bool bCreateOutput = false;
      72             : };
      73             : 
      74             : /************************************************************************/
      75             : /*                     GDALRasterizeOptionsGetParser()                  */
      76             : /************************************************************************/
      77             : 
      78             : static std::unique_ptr<GDALArgumentParser>
      79          29 : GDALRasterizeOptionsGetParser(GDALRasterizeOptions *psOptions,
      80             :                               GDALRasterizeOptionsForBinary *psOptionsForBinary)
      81             : {
      82             :     auto argParser = std::make_unique<GDALArgumentParser>(
      83          29 :         "gdal_rasterize", /* bForBinary=*/psOptionsForBinary != nullptr);
      84             : 
      85          29 :     argParser->add_description(_("Burns vector geometries into a raster."));
      86             : 
      87          29 :     argParser->add_epilog(
      88             :         _("This program burns vector geometries (points, lines, and polygons) "
      89          29 :           "into the raster band(s) of a raster image."));
      90             : 
      91             :     // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
      92          29 :     argParser->add_argument("-b")
      93          58 :         .metavar("<band>")
      94          29 :         .append()
      95          29 :         .scan<'i', int>()
      96             :         //.nargs(argparse::nargs_pattern::at_least_one)
      97          29 :         .help(_("The band(s) to burn values into."));
      98             : 
      99          29 :     argParser->add_argument("-i")
     100          29 :         .flag()
     101          29 :         .store_into(psOptions->bInverse)
     102          29 :         .help(_("Invert rasterization."));
     103             : 
     104          29 :     argParser->add_argument("-at")
     105          29 :         .flag()
     106             :         .action(
     107           1 :             [psOptions](const std::string &) {
     108             :                 psOptions->aosRasterizeOptions.SetNameValue("ALL_TOUCHED",
     109           1 :                                                             "TRUE");
     110          29 :             })
     111          29 :         .help(_("Enables the ALL_TOUCHED rasterization option."));
     112             : 
     113             :     // Mutually exclusive options: -burn, -3d, -a
     114             :     {
     115             :         // Required if options for binary
     116          29 :         auto &group = argParser->add_mutually_exclusive_group(
     117          29 :             psOptionsForBinary != nullptr);
     118             : 
     119             :         // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
     120          29 :         group.add_argument("-burn")
     121          58 :             .metavar("<value>")
     122          29 :             .scan<'g', double>()
     123          29 :             .append()
     124             :             //.nargs(argparse::nargs_pattern::at_least_one)
     125          29 :             .help(_("A fixed value to burn into the raster band(s)."));
     126             : 
     127          29 :         group.add_argument("-a")
     128          58 :             .metavar("<attribute_name>")
     129          29 :             .store_into(psOptions->osBurnAttribute)
     130             :             .help(_("Name of the field in the input layer to get the burn "
     131          29 :                     "values from."));
     132             : 
     133          29 :         group.add_argument("-3d")
     134          29 :             .flag()
     135          29 :             .store_into(psOptions->b3D)
     136             :             .action(
     137           3 :                 [psOptions](const std::string &) {
     138             :                     psOptions->aosRasterizeOptions.SetNameValue(
     139           3 :                         "BURN_VALUE_FROM", "Z");
     140          29 :                 })
     141             :             .help(_("Indicates that a burn value should be extracted from the "
     142          29 :                     "\"Z\" values of the feature."));
     143             :     }
     144             : 
     145          29 :     argParser->add_argument("-add")
     146          29 :         .flag()
     147             :         .action(
     148           0 :             [psOptions](const std::string &) {
     149           0 :                 psOptions->aosRasterizeOptions.SetNameValue("MERGE_ALG", "ADD");
     150          29 :             })
     151             :         .help(_("Instead of burning a new value, this adds the new value to "
     152          29 :                 "the existing raster."));
     153             : 
     154             :     // Undocumented
     155          29 :     argParser->add_argument("-chunkysize")
     156          29 :         .flag()
     157          29 :         .hidden()
     158             :         .action(
     159           0 :             [psOptions](const std::string &s) {
     160             :                 psOptions->aosRasterizeOptions.SetNameValue("CHUNKYSIZE",
     161           0 :                                                             s.c_str());
     162          29 :             });
     163             : 
     164             :     // Mutually exclusive -l, -sql
     165             :     {
     166          29 :         auto &group = argParser->add_mutually_exclusive_group(false);
     167             : 
     168          29 :         group.add_argument("-l")
     169          58 :             .metavar("<layer_name>")
     170          29 :             .append()
     171          29 :             .store_into(psOptions->aosLayers)
     172          29 :             .help(_("Name of the layer(s) to process."));
     173             : 
     174          29 :         group.add_argument("-sql")
     175          58 :             .metavar("<sql_statement>")
     176          29 :             .store_into(psOptions->osSQL)
     177             :             .action(
     178           3 :                 [psOptions](const std::string &sql)
     179             :                 {
     180           2 :                     GByte *pabyRet = nullptr;
     181           3 :                     if (!sql.empty() && sql.at(0) == '@' &&
     182           3 :                         VSIIngestFile(nullptr, sql.substr(1).c_str(), &pabyRet,
     183             :                                       nullptr, 1024 * 1024))
     184             :                     {
     185           1 :                         GDALRemoveBOM(pabyRet);
     186           1 :                         char *pszSQLStatement =
     187             :                             reinterpret_cast<char *>(pabyRet);
     188           1 :                         psOptions->osSQL = CPLStrdup(
     189           2 :                             CPLRemoveSQLComments(pszSQLStatement).c_str());
     190           1 :                         VSIFree(pszSQLStatement);
     191             :                     }
     192          31 :                 })
     193             :             .help(
     194             :                 _("An SQL statement to be evaluated against the datasource to "
     195          29 :                   "produce a virtual layer of features to be burned in."));
     196             :     }
     197             : 
     198          29 :     argParser->add_argument("-where")
     199          58 :         .metavar("<expression>")
     200          29 :         .store_into(psOptions->osWHERE)
     201             :         .help(_("An optional SQL WHERE style query expression to be applied to "
     202             :                 "select features "
     203          29 :                 "to burn in from the input layer(s)."));
     204             : 
     205          29 :     argParser->add_argument("-dialect")
     206          58 :         .metavar("<sql_dialect>")
     207          29 :         .store_into(psOptions->osDialect)
     208          29 :         .help(_("The SQL dialect to use for the SQL expression."));
     209             : 
     210             :     // Store later
     211          29 :     argParser->add_argument("-a_nodata")
     212          58 :         .metavar("<value>")
     213          29 :         .help(_("Assign a specified nodata value to output bands."));
     214             : 
     215             :     // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
     216          29 :     argParser->add_argument("-init")
     217          58 :         .metavar("<value>")
     218          29 :         .append()
     219             :         //.nargs(argparse::nargs_pattern::at_least_one)
     220          29 :         .scan<'g', double>()
     221          29 :         .help(_("Initialize the output bands to the specified value."));
     222             : 
     223          29 :     argParser->add_argument("-a_srs")
     224          58 :         .metavar("<srs_def>")
     225             :         .action(
     226           2 :             [psOptions](const std::string &osOutputSRSDef)
     227             :             {
     228           1 :                 if (psOptions->oOutputSRS.SetFromUserInput(
     229           1 :                         osOutputSRSDef.c_str()) != OGRERR_NONE)
     230             :                 {
     231             :                     throw std::invalid_argument(
     232           0 :                         std::string("Failed to process SRS definition: ")
     233           0 :                             .append(osOutputSRSDef));
     234             :                 }
     235           1 :                 psOptions->bCreateOutput = true;
     236          30 :             })
     237          29 :         .help(_("The spatial reference system to use for the output raster."));
     238             : 
     239          29 :     argParser->add_argument("-to")
     240          58 :         .metavar("<NAME>=<VALUE>")
     241          29 :         .append()
     242           1 :         .action([psOptions](const std::string &s)
     243          30 :                 { psOptions->aosTO.AddString(s.c_str()); })
     244          29 :         .help(_("Set a transformer option."));
     245             : 
     246             :     // Store later
     247          29 :     argParser->add_argument("-te")
     248          58 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
     249          29 :         .nargs(4)
     250          29 :         .scan<'g', double>()
     251          29 :         .help(_("Set georeferenced extents of output file to be created."));
     252             : 
     253             :     // Mutex with tr
     254             :     {
     255          29 :         auto &group = argParser->add_mutually_exclusive_group(false);
     256             : 
     257             :         // Store later
     258          29 :         group.add_argument("-tr")
     259          58 :             .metavar("<xres> <yres>")
     260          29 :             .nargs(2)
     261          29 :             .scan<'g', double>()
     262             :             .help(
     263          29 :                 _("Set output file resolution in target georeferenced units."));
     264             : 
     265             :         // Store later
     266          29 :         auto &arg = group.add_argument("-ts")
     267          58 :                         .metavar("<width> <height>")
     268          29 :                         .nargs(2)
     269          29 :                         .scan<'i', int>()
     270          29 :                         .help(_("Set output file size in pixels and lines."));
     271             : 
     272          29 :         argParser->add_hidden_alias_for(arg, "-outsize");
     273             :     }
     274             : 
     275          29 :     argParser->add_argument("-tap")
     276          29 :         .flag()
     277          29 :         .store_into(psOptions->bTargetAlignedPixels)
     278           0 :         .action([psOptions](const std::string &)
     279          29 :                 { psOptions->bCreateOutput = true; })
     280             :         .help(_("Align the coordinates of the extent to the values of the "
     281          29 :                 "output raster."));
     282             : 
     283          29 :     argParser->add_argument("-optim")
     284          58 :         .metavar("AUTO|VECTOR|RASTER")
     285             :         .action(
     286           3 :             [psOptions](const std::string &s) {
     287           3 :                 psOptions->aosRasterizeOptions.SetNameValue("OPTIM", s.c_str());
     288          29 :             })
     289          29 :         .help(_("Force the algorithm used."));
     290             : 
     291          29 :     argParser->add_creation_options_argument(psOptions->aosCreationOptions)
     292           0 :         .action([psOptions](const std::string &)
     293          29 :                 { psOptions->bCreateOutput = true; });
     294             : 
     295          29 :     argParser->add_output_type_argument(psOptions->eOutputType)
     296           2 :         .action([psOptions](const std::string &)
     297          29 :                 { psOptions->bCreateOutput = true; });
     298             : 
     299          29 :     argParser->add_output_format_argument(psOptions->osFormat)
     300           6 :         .action([psOptions](const std::string &)
     301          29 :                 { psOptions->bCreateOutput = true; });
     302             : 
     303             :     // Written that way so that in library mode, users can still use the -q
     304             :     // switch, even if it has no effect
     305             :     argParser->add_quiet_argument(
     306          29 :         psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr);
     307             : 
     308          29 :     if (psOptionsForBinary)
     309             :     {
     310             : 
     311             :         argParser->add_open_options_argument(
     312           9 :             psOptionsForBinary->aosOpenOptions);
     313             : 
     314           9 :         argParser->add_argument("src_datasource")
     315          18 :             .metavar("<src_datasource>")
     316           9 :             .store_into(psOptionsForBinary->osSource)
     317           9 :             .help(_("Any vector supported readable datasource."));
     318             : 
     319           9 :         argParser->add_argument("dst_filename")
     320          18 :             .metavar("<dst_filename>")
     321           9 :             .store_into(psOptionsForBinary->osDest)
     322           9 :             .help(_("The GDAL raster supported output file."));
     323             :     }
     324             : 
     325          29 :     return argParser;
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                          GDALRasterizeAppGetParserUsage()            */
     330             : /************************************************************************/
     331             : 
     332           0 : std::string GDALRasterizeAppGetParserUsage()
     333             : {
     334             :     try
     335             :     {
     336           0 :         GDALRasterizeOptions sOptions;
     337           0 :         GDALRasterizeOptionsForBinary sOptionsForBinary;
     338             :         auto argParser =
     339           0 :             GDALRasterizeOptionsGetParser(&sOptions, &sOptionsForBinary);
     340           0 :         return argParser->usage();
     341             :     }
     342           0 :     catch (const std::exception &err)
     343             :     {
     344           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     345           0 :                  err.what());
     346           0 :         return std::string();
     347             :     }
     348             : }
     349             : 
     350             : /************************************************************************/
     351             : /*                          InvertGeometries()                          */
     352             : /************************************************************************/
     353             : 
     354           2 : static void InvertGeometries(GDALDatasetH hDstDS,
     355             :                              std::vector<OGRGeometryH> &ahGeometries)
     356             : 
     357             : {
     358           2 :     OGRMultiPolygon *poInvertMP = new OGRMultiPolygon();
     359             : 
     360             :     /* -------------------------------------------------------------------- */
     361             :     /*      Create a ring that is a bit outside the raster dataset.         */
     362             :     /* -------------------------------------------------------------------- */
     363           2 :     const int brx = GDALGetRasterXSize(hDstDS) + 2;
     364           2 :     const int bry = GDALGetRasterYSize(hDstDS) + 2;
     365             : 
     366           2 :     double adfGeoTransform[6] = {};
     367           2 :     GDALGetGeoTransform(hDstDS, adfGeoTransform);
     368             : 
     369           2 :     auto poUniverseRing = std::make_unique<OGRLinearRing>();
     370             : 
     371           2 :     poUniverseRing->addPoint(
     372           2 :         adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
     373           2 :         adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
     374             : 
     375           2 :     poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
     376           2 :                                  -2 * adfGeoTransform[2],
     377           2 :                              adfGeoTransform[3] + brx * adfGeoTransform[4] +
     378           2 :                                  -2 * adfGeoTransform[5]);
     379             : 
     380           2 :     poUniverseRing->addPoint(adfGeoTransform[0] + brx * adfGeoTransform[1] +
     381           2 :                                  bry * adfGeoTransform[2],
     382           2 :                              adfGeoTransform[3] + brx * adfGeoTransform[4] +
     383           2 :                                  bry * adfGeoTransform[5]);
     384             : 
     385           2 :     poUniverseRing->addPoint(adfGeoTransform[0] + -2 * adfGeoTransform[1] +
     386           2 :                                  bry * adfGeoTransform[2],
     387           2 :                              adfGeoTransform[3] + -2 * adfGeoTransform[4] +
     388           2 :                                  bry * adfGeoTransform[5]);
     389             : 
     390           2 :     poUniverseRing->addPoint(
     391           2 :         adfGeoTransform[0] + -2 * adfGeoTransform[1] + -2 * adfGeoTransform[2],
     392           2 :         adfGeoTransform[3] + -2 * adfGeoTransform[4] + -2 * adfGeoTransform[5]);
     393             : 
     394           2 :     auto poUniversePoly = std::make_unique<OGRPolygon>();
     395           2 :     poUniversePoly->addRing(std::move(poUniverseRing));
     396           2 :     poInvertMP->addGeometry(std::move(poUniversePoly));
     397             : 
     398           2 :     bool bFoundNonPoly = false;
     399             :     // If we have GEOS, use it to "subtract" each polygon from the universe
     400             :     // multipolygon
     401           2 :     if (OGRGeometryFactory::haveGEOS())
     402             :     {
     403           2 :         OGRGeometry *poInvertMPAsGeom = poInvertMP;
     404           2 :         poInvertMP = nullptr;
     405           2 :         CPL_IGNORE_RET_VAL(poInvertMP);
     406           6 :         for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
     407             :         {
     408           4 :             auto poGeom = OGRGeometry::FromHandle(ahGeometries[iGeom]);
     409           4 :             const auto eGType = OGR_GT_Flatten(poGeom->getGeometryType());
     410           4 :             if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
     411             :             {
     412           1 :                 if (!bFoundNonPoly)
     413             :                 {
     414           1 :                     bFoundNonPoly = true;
     415           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     416             :                              "Ignoring non-polygon geometries in -i mode");
     417             :                 }
     418             :             }
     419             :             else
     420             :             {
     421           3 :                 auto poNewGeom = poInvertMPAsGeom->Difference(poGeom);
     422           3 :                 if (poNewGeom)
     423             :                 {
     424           3 :                     delete poInvertMPAsGeom;
     425           3 :                     poInvertMPAsGeom = poNewGeom;
     426             :                 }
     427             :             }
     428             : 
     429           4 :             delete poGeom;
     430             :         }
     431             : 
     432           2 :         ahGeometries.resize(1);
     433           2 :         ahGeometries[0] = OGRGeometry::ToHandle(poInvertMPAsGeom);
     434           2 :         return;
     435             :     }
     436             : 
     437             :     OGRPolygon &hUniversePoly =
     438           0 :         *poInvertMP->getGeometryRef(poInvertMP->getNumGeometries() - 1);
     439             : 
     440             :     /* -------------------------------------------------------------------- */
     441             :     /*      If we don't have GEOS, add outer rings of polygons as inner     */
     442             :     /*      rings of poUniversePoly and inner rings as sub-polygons. Note   */
     443             :     /*      that this only works properly if the polygons are disjoint, in  */
     444             :     /*      the sense that the outer ring of any polygon is not inside the  */
     445             :     /*      outer ring of another one. So the scenario of                   */
     446             :     /*      https://github.com/OSGeo/gdal/issues/8689 with an "island" in   */
     447             :     /*      the middle of a hole will not work properly.                    */
     448             :     /* -------------------------------------------------------------------- */
     449           0 :     for (unsigned int iGeom = 0; iGeom < ahGeometries.size(); iGeom++)
     450             :     {
     451             :         const auto eGType =
     452           0 :             OGR_GT_Flatten(OGR_G_GetGeometryType(ahGeometries[iGeom]));
     453           0 :         if (eGType != wkbPolygon && eGType != wkbMultiPolygon)
     454             :         {
     455           0 :             if (!bFoundNonPoly)
     456             :             {
     457           0 :                 bFoundNonPoly = true;
     458           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     459             :                          "Ignoring non-polygon geometries in -i mode");
     460             :             }
     461           0 :             OGR_G_DestroyGeometry(ahGeometries[iGeom]);
     462           0 :             continue;
     463             :         }
     464             : 
     465             :         const auto ProcessPoly =
     466           0 :             [&hUniversePoly, poInvertMP](OGRPolygon *poPoly)
     467             :         {
     468           0 :             for (int i = poPoly->getNumInteriorRings() - 1; i >= 0; --i)
     469             :             {
     470           0 :                 auto poNewPoly = std::make_unique<OGRPolygon>();
     471             :                 std::unique_ptr<OGRLinearRing> poRing(
     472           0 :                     poPoly->stealInteriorRing(i));
     473           0 :                 poNewPoly->addRing(std::move(poRing));
     474           0 :                 poInvertMP->addGeometry(std::move(poNewPoly));
     475             :             }
     476           0 :             std::unique_ptr<OGRLinearRing> poShell(poPoly->stealExteriorRing());
     477           0 :             hUniversePoly.addRing(std::move(poShell));
     478           0 :         };
     479             : 
     480           0 :         if (eGType == wkbPolygon)
     481             :         {
     482             :             auto poPoly =
     483           0 :                 OGRGeometry::FromHandle(ahGeometries[iGeom])->toPolygon();
     484           0 :             ProcessPoly(poPoly);
     485           0 :             delete poPoly;
     486             :         }
     487             :         else
     488             :         {
     489             :             auto poMulti =
     490           0 :                 OGRGeometry::FromHandle(ahGeometries[iGeom])->toMultiPolygon();
     491           0 :             for (auto *poPoly : *poMulti)
     492             :             {
     493           0 :                 ProcessPoly(poPoly);
     494             :             }
     495           0 :             delete poMulti;
     496             :         }
     497             :     }
     498             : 
     499           0 :     ahGeometries.resize(1);
     500           0 :     ahGeometries[0] = OGRGeometry::ToHandle(poInvertMP);
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*                            ProcessLayer()                            */
     505             : /*                                                                      */
     506             : /*      Process all the features in a layer selection, collecting       */
     507             : /*      geometries and burn values.                                     */
     508             : /************************************************************************/
     509             : 
     510          24 : static CPLErr ProcessLayer(OGRLayerH hSrcLayer, bool bSRSIsSet,
     511             :                            GDALDatasetH hDstDS,
     512             :                            const std::vector<int> &anBandList,
     513             :                            const std::vector<double> &adfBurnValues, bool b3D,
     514             :                            bool bInverse, const std::string &osBurnAttribute,
     515             :                            CSLConstList papszRasterizeOptions,
     516             :                            CSLConstList papszTO, GDALProgressFunc pfnProgress,
     517             :                            void *pProgressData)
     518             : 
     519             : {
     520             :     /* -------------------------------------------------------------------- */
     521             :     /*      Checkout that SRS are the same.                                 */
     522             :     /*      If -a_srs is specified, skip the test                           */
     523             :     /* -------------------------------------------------------------------- */
     524          24 :     OGRCoordinateTransformationH hCT = nullptr;
     525          24 :     if (!bSRSIsSet)
     526             :     {
     527          23 :         OGRSpatialReferenceH hDstSRS = GDALGetSpatialRef(hDstDS);
     528             : 
     529          23 :         if (hDstSRS)
     530          11 :             hDstSRS = OSRClone(hDstSRS);
     531          12 :         else if (GDALGetMetadata(hDstDS, "RPC") != nullptr)
     532             :         {
     533           2 :             hDstSRS = OSRNewSpatialReference(nullptr);
     534           2 :             CPL_IGNORE_RET_VAL(
     535           2 :                 OSRSetFromUserInput(hDstSRS, SRS_WKT_WGS84_LAT_LONG));
     536           2 :             OSRSetAxisMappingStrategy(hDstSRS, OAMS_TRADITIONAL_GIS_ORDER);
     537             :         }
     538             : 
     539          23 :         OGRSpatialReferenceH hSrcSRS = OGR_L_GetSpatialRef(hSrcLayer);
     540          23 :         if (hDstSRS != nullptr && hSrcSRS != nullptr)
     541             :         {
     542          13 :             if (OSRIsSame(hSrcSRS, hDstSRS) == FALSE)
     543             :             {
     544           1 :                 hCT = OCTNewCoordinateTransformation(hSrcSRS, hDstSRS);
     545           1 :                 if (hCT == nullptr)
     546             :                 {
     547           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     548             :                              "The output raster dataset and the input vector "
     549             :                              "layer do not have the same SRS.\n"
     550             :                              "And reprojection of input data did not work. "
     551             :                              "Results might be incorrect.");
     552             :                 }
     553             :             }
     554             :         }
     555          10 :         else if (hDstSRS != nullptr && hSrcSRS == nullptr)
     556             :         {
     557           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     558             :                      "The output raster dataset has a SRS, but the input "
     559             :                      "vector layer SRS is unknown.\n"
     560             :                      "Ensure input vector has the same SRS, otherwise results "
     561             :                      "might be incorrect.");
     562             :         }
     563          10 :         else if (hDstSRS == nullptr && hSrcSRS != nullptr)
     564             :         {
     565           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     566             :                      "The input vector layer has a SRS, but the output raster "
     567             :                      "dataset SRS is unknown.\n"
     568             :                      "Ensure output raster dataset has the same SRS, otherwise "
     569             :                      "results might be incorrect.");
     570             :         }
     571             : 
     572          23 :         if (hDstSRS != nullptr)
     573             :         {
     574          13 :             OSRDestroySpatialReference(hDstSRS);
     575             :         }
     576             :     }
     577             : 
     578             :     /* -------------------------------------------------------------------- */
     579             :     /*      Get field index, and check.                                     */
     580             :     /* -------------------------------------------------------------------- */
     581          24 :     int iBurnField = -1;
     582          24 :     bool bUseInt64 = false;
     583          24 :     if (!osBurnAttribute.empty())
     584             :     {
     585           3 :         OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hSrcLayer);
     586           3 :         iBurnField = OGR_FD_GetFieldIndex(hLayerDefn, osBurnAttribute.c_str());
     587           3 :         if (iBurnField == -1)
     588             :         {
     589           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     590             :                      "Failed to find field %s on layer %s.",
     591             :                      osBurnAttribute.c_str(),
     592             :                      OGR_FD_GetName(OGR_L_GetLayerDefn(hSrcLayer)));
     593           0 :             if (hCT != nullptr)
     594           0 :                 OCTDestroyCoordinateTransformation(hCT);
     595           0 :             return CE_Failure;
     596             :         }
     597           3 :         if (OGR_Fld_GetType(OGR_FD_GetFieldDefn(hLayerDefn, iBurnField)) ==
     598             :             OFTInteger64)
     599             :         {
     600           1 :             GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, anBandList[0]);
     601           1 :             if (hBand && GDALGetRasterDataType(hBand) == GDT_Int64)
     602             :             {
     603           1 :                 bUseInt64 = true;
     604             :             }
     605             :         }
     606             :     }
     607             : 
     608             :     /* -------------------------------------------------------------------- */
     609             :     /*      Collect the geometries from this layer, and build list of       */
     610             :     /*      burn values.                                                    */
     611             :     /* -------------------------------------------------------------------- */
     612          24 :     OGRFeatureH hFeat = nullptr;
     613          48 :     std::vector<OGRGeometryH> ahGeometries;
     614          48 :     std::vector<double> adfFullBurnValues;
     615          24 :     std::vector<int64_t> anFullBurnValues;
     616             : 
     617          24 :     OGR_L_ResetReading(hSrcLayer);
     618             : 
     619        1926 :     while ((hFeat = OGR_L_GetNextFeature(hSrcLayer)) != nullptr)
     620             :     {
     621        1902 :         OGRGeometryH hGeom = OGR_F_StealGeometry(hFeat);
     622        1902 :         if (hGeom == nullptr)
     623             :         {
     624           5 :             OGR_F_Destroy(hFeat);
     625           5 :             continue;
     626             :         }
     627             : 
     628        1897 :         if (hCT != nullptr)
     629             :         {
     630           1 :             if (OGR_G_Transform(hGeom, hCT) != OGRERR_NONE)
     631             :             {
     632           0 :                 OGR_F_Destroy(hFeat);
     633           0 :                 OGR_G_DestroyGeometry(hGeom);
     634           0 :                 continue;
     635             :             }
     636             :         }
     637        1897 :         ahGeometries.push_back(hGeom);
     638             : 
     639        3820 :         for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
     640             :         {
     641        1923 :             if (!adfBurnValues.empty())
     642          62 :                 adfFullBurnValues.push_back(adfBurnValues[std::min(
     643             :                     iBand,
     644         124 :                     static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
     645        1861 :             else if (!osBurnAttribute.empty())
     646             :             {
     647           7 :                 if (bUseInt64)
     648           1 :                     anFullBurnValues.push_back(
     649           1 :                         OGR_F_GetFieldAsInteger64(hFeat, iBurnField));
     650             :                 else
     651           6 :                     adfFullBurnValues.push_back(
     652           6 :                         OGR_F_GetFieldAsDouble(hFeat, iBurnField));
     653             :             }
     654        1854 :             else if (b3D)
     655             :             {
     656             :                 /* Points and Lines will have their "z" values collected at the
     657             :                    point and line levels respectively. Not implemented for
     658             :                    polygons */
     659        1854 :                 adfFullBurnValues.push_back(0.0);
     660             :             }
     661             :         }
     662             : 
     663        1897 :         OGR_F_Destroy(hFeat);
     664             :     }
     665             : 
     666          24 :     if (hCT != nullptr)
     667           1 :         OCTDestroyCoordinateTransformation(hCT);
     668             : 
     669             :     /* -------------------------------------------------------------------- */
     670             :     /*      If we are in inverse mode, we add one extra ring around the     */
     671             :     /*      whole dataset to invert the concept of insideness and then      */
     672             :     /*      merge everything into one geometry collection.                  */
     673             :     /* -------------------------------------------------------------------- */
     674          24 :     if (bInverse)
     675             :     {
     676           2 :         if (ahGeometries.empty())
     677             :         {
     678           0 :             for (unsigned int iBand = 0; iBand < anBandList.size(); iBand++)
     679             :             {
     680           0 :                 if (!adfBurnValues.empty())
     681           0 :                     adfFullBurnValues.push_back(adfBurnValues[std::min(
     682             :                         iBand,
     683           0 :                         static_cast<unsigned int>(adfBurnValues.size()) - 1)]);
     684             :                 else /* FIXME? Not sure what to do exactly in the else case, but
     685             :                         we must insert a value */
     686             :                 {
     687           0 :                     adfFullBurnValues.push_back(0.0);
     688           0 :                     anFullBurnValues.push_back(0);
     689             :                 }
     690             :             }
     691             :         }
     692             : 
     693           2 :         InvertGeometries(hDstDS, ahGeometries);
     694             :     }
     695             : 
     696             :     /* -------------------------------------------------------------------- */
     697             :     /*      If we have transformer options, create the transformer here     */
     698             :     /*      Coordinate transformation to the target SRS has already been    */
     699             :     /*      done, so we just need to convert to target raster space.        */
     700             :     /*      Note: this is somewhat identical to what is done in             */
     701             :     /*      GDALRasterizeGeometries() itself, except we can pass transformer*/
     702             :     /*      options.                                                        */
     703             :     /* -------------------------------------------------------------------- */
     704             : 
     705          24 :     void *pTransformArg = nullptr;
     706          24 :     GDALTransformerFunc pfnTransformer = nullptr;
     707          24 :     CPLErr eErr = CE_None;
     708          24 :     if (papszTO != nullptr)
     709             :     {
     710           1 :         GDALDataset *poDS = GDALDataset::FromHandle(hDstDS);
     711           1 :         char **papszTransformerOptions = CSLDuplicate(papszTO);
     712           1 :         double adfGeoTransform[6] = {0.0};
     713           1 :         if (poDS->GetGeoTransform(adfGeoTransform) != CE_None &&
     714           1 :             poDS->GetGCPCount() == 0 && poDS->GetMetadata("RPC") == nullptr)
     715             :         {
     716           0 :             papszTransformerOptions = CSLSetNameValue(
     717             :                 papszTransformerOptions, "DST_METHOD", "NO_GEOTRANSFORM");
     718             :         }
     719             : 
     720           1 :         pTransformArg = GDALCreateGenImgProjTransformer2(
     721             :             nullptr, hDstDS, papszTransformerOptions);
     722           1 :         CSLDestroy(papszTransformerOptions);
     723             : 
     724           1 :         pfnTransformer = GDALGenImgProjTransform;
     725           1 :         if (pTransformArg == nullptr)
     726             :         {
     727           0 :             eErr = CE_Failure;
     728             :         }
     729             :     }
     730             : 
     731             :     /* -------------------------------------------------------------------- */
     732             :     /*      Perform the burn.                                               */
     733             :     /* -------------------------------------------------------------------- */
     734          24 :     if (eErr == CE_None)
     735             :     {
     736          24 :         if (bUseInt64)
     737             :         {
     738           2 :             eErr = GDALRasterizeGeometriesInt64(
     739           1 :                 hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
     740           1 :                 static_cast<int>(ahGeometries.size()), ahGeometries.data(),
     741           1 :                 pfnTransformer, pTransformArg, anFullBurnValues.data(),
     742             :                 papszRasterizeOptions, pfnProgress, pProgressData);
     743             :         }
     744             :         else
     745             :         {
     746          46 :             eErr = GDALRasterizeGeometries(
     747          23 :                 hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
     748          23 :                 static_cast<int>(ahGeometries.size()), ahGeometries.data(),
     749          23 :                 pfnTransformer, pTransformArg, adfFullBurnValues.data(),
     750             :                 papszRasterizeOptions, pfnProgress, pProgressData);
     751             :         }
     752             :     }
     753             : 
     754             :     /* -------------------------------------------------------------------- */
     755             :     /*      Cleanup                                                         */
     756             :     /* -------------------------------------------------------------------- */
     757             : 
     758          24 :     if (pTransformArg)
     759           1 :         GDALDestroyTransformer(pTransformArg);
     760             : 
     761        1919 :     for (int iGeom = static_cast<int>(ahGeometries.size()) - 1; iGeom >= 0;
     762             :          iGeom--)
     763        1895 :         OGR_G_DestroyGeometry(ahGeometries[iGeom]);
     764             : 
     765          24 :     return eErr;
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                  CreateOutputDataset()                               */
     770             : /************************************************************************/
     771             : 
     772          12 : static GDALDatasetH CreateOutputDataset(
     773             :     const std::vector<OGRLayerH> &ahLayers, OGRSpatialReferenceH hSRS,
     774             :     OGREnvelope sEnvelop, GDALDriverH hDriver, const char *pszDest, int nXSize,
     775             :     int nYSize, double dfXRes, double dfYRes, bool bTargetAlignedPixels,
     776             :     int nBandCount, GDALDataType eOutputType, CSLConstList papszCreationOptions,
     777             :     const std::vector<double> &adfInitVals, const char *pszNoData)
     778             : {
     779          12 :     bool bFirstLayer = true;
     780          12 :     char *pszWKT = nullptr;
     781          12 :     const bool bBoundsSpecifiedByUser = sEnvelop.IsInit();
     782             : 
     783          23 :     for (unsigned int i = 0; i < ahLayers.size(); i++)
     784             :     {
     785          12 :         OGRLayerH hLayer = ahLayers[i];
     786             : 
     787          12 :         if (!bBoundsSpecifiedByUser)
     788             :         {
     789          10 :             OGREnvelope sLayerEnvelop;
     790             : 
     791          10 :             if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
     792             :             {
     793           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     794             :                          "Cannot get layer extent");
     795           1 :                 return nullptr;
     796             :             }
     797             : 
     798             :             /* Voluntarily increase the extent by a half-pixel size to avoid */
     799             :             /* missing points on the border */
     800           9 :             if (!bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
     801             :             {
     802           5 :                 sLayerEnvelop.MinX -= dfXRes / 2;
     803           5 :                 sLayerEnvelop.MaxX += dfXRes / 2;
     804           5 :                 sLayerEnvelop.MinY -= dfYRes / 2;
     805           5 :                 sLayerEnvelop.MaxY += dfYRes / 2;
     806             :             }
     807             : 
     808           9 :             sEnvelop.Merge(sLayerEnvelop);
     809             :         }
     810             : 
     811          11 :         if (bFirstLayer)
     812             :         {
     813          11 :             if (hSRS == nullptr)
     814          10 :                 hSRS = OGR_L_GetSpatialRef(hLayer);
     815             : 
     816          11 :             bFirstLayer = false;
     817             :         }
     818             :     }
     819             : 
     820          11 :     if (!sEnvelop.IsInit())
     821             :     {
     822           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
     823           0 :         return nullptr;
     824             :     }
     825             : 
     826          11 :     if (dfXRes == 0 && dfYRes == 0)
     827             :     {
     828           4 :         if (nXSize == 0 || nYSize == 0)
     829             :         {
     830           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     831             :                      "Size and resolutions are missing");
     832           1 :             return nullptr;
     833             :         }
     834           3 :         dfXRes = (sEnvelop.MaxX - sEnvelop.MinX) / nXSize;
     835           3 :         dfYRes = (sEnvelop.MaxY - sEnvelop.MinY) / nYSize;
     836             :     }
     837           7 :     else if (bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
     838             :     {
     839           0 :         sEnvelop.MinX = floor(sEnvelop.MinX / dfXRes) * dfXRes;
     840           0 :         sEnvelop.MaxX = ceil(sEnvelop.MaxX / dfXRes) * dfXRes;
     841           0 :         sEnvelop.MinY = floor(sEnvelop.MinY / dfYRes) * dfYRes;
     842           0 :         sEnvelop.MaxY = ceil(sEnvelop.MaxY / dfYRes) * dfYRes;
     843             :     }
     844             : 
     845          10 :     if (dfXRes == 0 || dfYRes == 0)
     846             :     {
     847           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
     848           0 :         return nullptr;
     849             :     }
     850             : 
     851          10 :     double adfProjection[6] = {sEnvelop.MinX, dfXRes, 0.0,
     852          10 :                                sEnvelop.MaxY, 0.0,    -dfYRes};
     853             : 
     854          10 :     if (nXSize == 0 && nYSize == 0)
     855             :     {
     856             :         // coverity[divide_by_zero]
     857           7 :         const double dfXSize = 0.5 + (sEnvelop.MaxX - sEnvelop.MinX) / dfXRes;
     858             :         // coverity[divide_by_zero]
     859           7 :         const double dfYSize = 0.5 + (sEnvelop.MaxY - sEnvelop.MinY) / dfYRes;
     860           7 :         if (dfXSize > std::numeric_limits<int>::max() ||
     861           6 :             dfXSize < std::numeric_limits<int>::min() ||
     862          18 :             dfYSize > std::numeric_limits<int>::max() ||
     863           5 :             dfYSize < std::numeric_limits<int>::min())
     864             :         {
     865           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     866             :                      "Invalid computed output raster size: %f x %f", dfXSize,
     867             :                      dfYSize);
     868           2 :             return nullptr;
     869             :         }
     870           5 :         nXSize = static_cast<int>(dfXSize);
     871           5 :         nYSize = static_cast<int>(dfYSize);
     872             :     }
     873             : 
     874             :     GDALDatasetH hDstDS =
     875           8 :         GDALCreate(hDriver, pszDest, nXSize, nYSize, nBandCount, eOutputType,
     876             :                    papszCreationOptions);
     877           8 :     if (hDstDS == nullptr)
     878             :     {
     879           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszDest);
     880           0 :         return nullptr;
     881             :     }
     882             : 
     883           8 :     GDALSetGeoTransform(hDstDS, adfProjection);
     884             : 
     885           8 :     if (hSRS)
     886           3 :         OSRExportToWkt(hSRS, &pszWKT);
     887           8 :     if (pszWKT)
     888           3 :         GDALSetProjection(hDstDS, pszWKT);
     889           8 :     CPLFree(pszWKT);
     890             : 
     891             :     /*if( nBandCount == 3 || nBandCount == 4 )
     892             :     {
     893             :         for( int iBand = 0; iBand < nBandCount; iBand++ )
     894             :         {
     895             :             GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
     896             :             GDALSetRasterColorInterpretation(hBand,
     897             :     (GDALColorInterp)(GCI_RedBand + iBand));
     898             :         }
     899             :     }*/
     900             : 
     901           8 :     if (pszNoData)
     902             :     {
     903          16 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     904             :         {
     905           8 :             GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
     906           8 :             if (GDALGetRasterDataType(hBand) == GDT_Int64)
     907           1 :                 GDALSetRasterNoDataValueAsInt64(hBand,
     908           1 :                                                 CPLAtoGIntBig(pszNoData));
     909             :             else
     910           7 :                 GDALSetRasterNoDataValue(hBand, CPLAtof(pszNoData));
     911             :         }
     912             :     }
     913             : 
     914           8 :     if (!adfInitVals.empty())
     915             :     {
     916           6 :         for (int iBand = 0;
     917           6 :              iBand < std::min(nBandCount, static_cast<int>(adfInitVals.size()));
     918             :              iBand++)
     919             :         {
     920           3 :             GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1);
     921           3 :             GDALFillRaster(hBand, adfInitVals[iBand], 0);
     922             :         }
     923             :     }
     924             : 
     925           8 :     return hDstDS;
     926             : }
     927             : 
     928             : /************************************************************************/
     929             : /*                             GDALRasterize()                          */
     930             : /************************************************************************/
     931             : 
     932             : /* clang-format off */
     933             : /**
     934             :  * Burns vector geometries into a raster
     935             :  *
     936             :  * This is the equivalent of the
     937             :  * <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
     938             :  *
     939             :  * GDALRasterizeOptions* must be allocated and freed with
     940             :  * GDALRasterizeOptionsNew() and GDALRasterizeOptionsFree() respectively.
     941             :  * pszDest and hDstDS cannot be used at the same time.
     942             :  *
     943             :  * @param pszDest the destination dataset path or NULL.
     944             :  * @param hDstDS the destination dataset or NULL.
     945             :  * @param hSrcDataset the source dataset handle.
     946             :  * @param psOptionsIn the options struct returned by GDALRasterizeOptionsNew()
     947             :  * or NULL.
     948             :  * @param pbUsageError pointer to a integer output variable to store if any
     949             :  * usage error has occurred or NULL.
     950             :  * @return the output dataset (new dataset that must be closed using
     951             :  * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
     952             :  *
     953             :  * @since GDAL 2.1
     954             :  */
     955             : /* clang-format on */
     956             : 
     957          29 : GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS,
     958             :                            GDALDatasetH hSrcDataset,
     959             :                            const GDALRasterizeOptions *psOptionsIn,
     960             :                            int *pbUsageError)
     961             : {
     962          29 :     if (pszDest == nullptr && hDstDS == nullptr)
     963             :     {
     964           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     965             :                  "pszDest == NULL && hDstDS == NULL");
     966             : 
     967           0 :         if (pbUsageError)
     968           0 :             *pbUsageError = TRUE;
     969           0 :         return nullptr;
     970             :     }
     971          29 :     if (hSrcDataset == nullptr)
     972             :     {
     973           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
     974             : 
     975           0 :         if (pbUsageError)
     976           0 :             *pbUsageError = TRUE;
     977           0 :         return nullptr;
     978             :     }
     979          29 :     if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
     980             :     {
     981           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     982             :                  "hDstDS != NULL but options that imply creating a new dataset "
     983             :                  "have been set.");
     984             : 
     985           0 :         if (pbUsageError)
     986           0 :             *pbUsageError = TRUE;
     987           0 :         return nullptr;
     988             :     }
     989             : 
     990          29 :     GDALRasterizeOptions *psOptionsToFree = nullptr;
     991          29 :     const GDALRasterizeOptions *psOptions = psOptionsIn;
     992          29 :     if (psOptions == nullptr)
     993             :     {
     994           0 :         psOptionsToFree = GDALRasterizeOptionsNew(nullptr, nullptr);
     995           0 :         psOptions = psOptionsToFree;
     996             :     }
     997             : 
     998          29 :     const bool bCloseOutDSOnError = hDstDS == nullptr;
     999          29 :     if (pszDest == nullptr)
    1000          13 :         pszDest = GDALGetDescription(hDstDS);
    1001             : 
    1002          44 :     if (psOptions->osSQL.empty() && psOptions->aosLayers.empty() &&
    1003          15 :         GDALDatasetGetLayerCount(hSrcDataset) != 1)
    1004             :     {
    1005           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1006             :                  "Neither -sql nor -l are specified, but the source dataset "
    1007             :                  "has not one single layer.");
    1008           0 :         if (pbUsageError)
    1009           0 :             *pbUsageError = TRUE;
    1010           0 :         GDALRasterizeOptionsFree(psOptionsToFree);
    1011           0 :         return nullptr;
    1012             :     }
    1013             : 
    1014             :     /* -------------------------------------------------------------------- */
    1015             :     /*      Open target raster file.  Eventually we will add optional       */
    1016             :     /*      creation.                                                       */
    1017             :     /* -------------------------------------------------------------------- */
    1018          29 :     const bool bCreateOutput = psOptions->bCreateOutput || hDstDS == nullptr;
    1019             : 
    1020          29 :     GDALDriverH hDriver = nullptr;
    1021          29 :     if (bCreateOutput)
    1022             :     {
    1023          13 :         CPLString osFormat;
    1024          13 :         if (psOptions->osFormat.empty())
    1025             :         {
    1026           7 :             osFormat = GetOutputDriverForRaster(pszDest);
    1027           7 :             if (osFormat.empty())
    1028             :             {
    1029           0 :                 GDALRasterizeOptionsFree(psOptionsToFree);
    1030           0 :                 return nullptr;
    1031             :             }
    1032             :         }
    1033             :         else
    1034             :         {
    1035           6 :             osFormat = psOptions->osFormat;
    1036             :         }
    1037             : 
    1038             :         /* --------------------------------------------------------------------
    1039             :          */
    1040             :         /*      Find the output driver. */
    1041             :         /* --------------------------------------------------------------------
    1042             :          */
    1043          13 :         hDriver = GDALGetDriverByName(osFormat);
    1044             :         char **papszDriverMD =
    1045          13 :             hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
    1046          39 :         if (hDriver == nullptr ||
    1047          13 :             !CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER,
    1048          26 :                                               "FALSE")) ||
    1049          13 :             !CPLTestBool(
    1050             :                 CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
    1051             :         {
    1052           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1053             :                      "Output driver `%s' not recognised or does not support "
    1054             :                      "direct output file creation.",
    1055             :                      osFormat.c_str());
    1056           0 :             GDALRasterizeOptionsFree(psOptionsToFree);
    1057           0 :             return nullptr;
    1058             :         }
    1059             :     }
    1060             : 
    1061          10 :     const auto GetOutputDataType = [&](OGRLayerH hLayer)
    1062             :     {
    1063          10 :         CPLAssert(bCreateOutput);
    1064          10 :         CPLAssert(hDriver);
    1065          21 :         GDALDataType eOutputType = psOptions->eOutputType;
    1066          10 :         if (eOutputType == GDT_Unknown && !psOptions->osBurnAttribute.empty())
    1067             :         {
    1068           1 :             OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer);
    1069           1 :             const int iBurnField = OGR_FD_GetFieldIndex(
    1070           1 :                 hLayerDefn, psOptions->osBurnAttribute.c_str());
    1071           1 :             if (iBurnField >= 0 && OGR_Fld_GetType(OGR_FD_GetFieldDefn(
    1072             :                                        hLayerDefn, iBurnField)) == OFTInteger64)
    1073             :             {
    1074           1 :                 const char *pszMD = GDALGetMetadataItem(
    1075             :                     hDriver, GDAL_DMD_CREATIONDATATYPES, nullptr);
    1076           2 :                 if (pszMD && CPLStringList(CSLTokenizeString2(pszMD, " ", 0))
    1077           1 :                                      .FindString("Int64") >= 0)
    1078             :                 {
    1079           1 :                     eOutputType = GDT_Int64;
    1080             :                 }
    1081             :             }
    1082             :         }
    1083          10 :         if (eOutputType == GDT_Unknown)
    1084             :         {
    1085           9 :             eOutputType = GDT_Float64;
    1086             :         }
    1087          10 :         return eOutputType;
    1088          29 :     };
    1089             : 
    1090             :     // Store SRS handle
    1091             :     OGRSpatialReferenceH hSRS =
    1092          29 :         psOptions->oOutputSRS.IsEmpty()
    1093          29 :             ? nullptr
    1094           1 :             : OGRSpatialReference::ToHandle(
    1095           1 :                   const_cast<OGRSpatialReference *>(&psOptions->oOutputSRS));
    1096             : 
    1097             :     /* -------------------------------------------------------------------- */
    1098             :     /*      Process SQL request.                                            */
    1099             :     /* -------------------------------------------------------------------- */
    1100          29 :     CPLErr eErr = CE_Failure;
    1101             : 
    1102          29 :     if (!psOptions->osSQL.empty())
    1103             :     {
    1104             :         OGRLayerH hLayer =
    1105           2 :             GDALDatasetExecuteSQL(hSrcDataset, psOptions->osSQL.c_str(),
    1106           2 :                                   nullptr, psOptions->osDialect.c_str());
    1107           2 :         if (hLayer != nullptr)
    1108             :         {
    1109           2 :             if (bCreateOutput)
    1110             :             {
    1111           2 :                 std::vector<OGRLayerH> ahLayers;
    1112           2 :                 ahLayers.push_back(hLayer);
    1113             : 
    1114           2 :                 const GDALDataType eOutputType = GetOutputDataType(hLayer);
    1115           2 :                 hDstDS = CreateOutputDataset(
    1116           2 :                     ahLayers, hSRS, psOptions->sEnvelop, hDriver, pszDest,
    1117           2 :                     psOptions->nXSize, psOptions->nYSize, psOptions->dfXRes,
    1118           2 :                     psOptions->dfYRes, psOptions->bTargetAlignedPixels,
    1119           2 :                     static_cast<int>(psOptions->anBandList.size()), eOutputType,
    1120           2 :                     psOptions->aosCreationOptions, psOptions->adfInitVals,
    1121           2 :                     psOptions->osNoData.c_str());
    1122           2 :                 if (hDstDS == nullptr)
    1123             :                 {
    1124           0 :                     GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
    1125           0 :                     GDALRasterizeOptionsFree(psOptionsToFree);
    1126           0 :                     return nullptr;
    1127             :                 }
    1128             :             }
    1129             : 
    1130           6 :             eErr = ProcessLayer(
    1131           2 :                 hLayer, hSRS != nullptr, hDstDS, psOptions->anBandList,
    1132           2 :                 psOptions->adfBurnValues, psOptions->b3D, psOptions->bInverse,
    1133           2 :                 psOptions->osBurnAttribute.c_str(),
    1134           2 :                 psOptions->aosRasterizeOptions, psOptions->aosTO,
    1135           2 :                 psOptions->pfnProgress, psOptions->pProgressData);
    1136             : 
    1137           2 :             GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
    1138             :         }
    1139             :     }
    1140             : 
    1141             :     /* -------------------------------------------------------------------- */
    1142             :     /*      Create output file if necessary.                                */
    1143             :     /* -------------------------------------------------------------------- */
    1144             :     const int nLayerCount =
    1145          56 :         (psOptions->osSQL.empty() && psOptions->aosLayers.empty())
    1146          58 :             ? 1
    1147          14 :             : static_cast<int>(psOptions->aosLayers.size());
    1148             : 
    1149          29 :     if (bCreateOutput && hDstDS == nullptr)
    1150             :     {
    1151          11 :         std::vector<OGRLayerH> ahLayers;
    1152             : 
    1153          11 :         GDALDataType eOutputType = psOptions->eOutputType;
    1154             : 
    1155          21 :         for (int i = 0; i < nLayerCount; i++)
    1156             :         {
    1157             :             OGRLayerH hLayer;
    1158          11 :             if (psOptions->aosLayers.size() > static_cast<size_t>(i))
    1159           5 :                 hLayer = GDALDatasetGetLayerByName(
    1160           5 :                     hSrcDataset, psOptions->aosLayers[i].c_str());
    1161             :             else
    1162           6 :                 hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
    1163          11 :             if (hLayer == nullptr)
    1164             :             {
    1165           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1166             :                          "Unable to find layer \"%s\".",
    1167           1 :                          psOptions->aosLayers.size() > static_cast<size_t>(i)
    1168           1 :                              ? psOptions->aosLayers[i].c_str()
    1169             :                              : "0");
    1170           1 :                 GDALRasterizeOptionsFree(psOptionsToFree);
    1171           1 :                 return nullptr;
    1172             :             }
    1173          10 :             if (eOutputType == GDT_Unknown)
    1174             :             {
    1175           8 :                 if (GetOutputDataType(hLayer) == GDT_Int64)
    1176           1 :                     eOutputType = GDT_Int64;
    1177             :             }
    1178             : 
    1179          10 :             ahLayers.push_back(hLayer);
    1180             :         }
    1181             : 
    1182          10 :         if (eOutputType == GDT_Unknown)
    1183             :         {
    1184           7 :             eOutputType = GDT_Float64;
    1185             :         }
    1186             : 
    1187          10 :         hDstDS = CreateOutputDataset(
    1188          10 :             ahLayers, hSRS, psOptions->sEnvelop, hDriver, pszDest,
    1189          10 :             psOptions->nXSize, psOptions->nYSize, psOptions->dfXRes,
    1190          10 :             psOptions->dfYRes, psOptions->bTargetAlignedPixels,
    1191          10 :             static_cast<int>(psOptions->anBandList.size()), eOutputType,
    1192          10 :             psOptions->aosCreationOptions, psOptions->adfInitVals,
    1193          10 :             psOptions->osNoData.c_str());
    1194          10 :         if (hDstDS == nullptr)
    1195             :         {
    1196           4 :             GDALRasterizeOptionsFree(psOptionsToFree);
    1197           4 :             return nullptr;
    1198             :         }
    1199             :     }
    1200             : 
    1201             :     /* -------------------------------------------------------------------- */
    1202             :     /*      Process each layer.                                             */
    1203             :     /* -------------------------------------------------------------------- */
    1204             : 
    1205          46 :     for (int i = 0; i < nLayerCount; i++)
    1206             :     {
    1207             :         OGRLayerH hLayer;
    1208          22 :         if (psOptions->aosLayers.size() > static_cast<size_t>(i))
    1209          11 :             hLayer = GDALDatasetGetLayerByName(hSrcDataset,
    1210          11 :                                                psOptions->aosLayers[i].c_str());
    1211             :         else
    1212          11 :             hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
    1213          22 :         if (hLayer == nullptr)
    1214             :         {
    1215           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1216             :                      "Unable to find layer \"%s\".",
    1217           0 :                      psOptions->aosLayers.size() > static_cast<size_t>(i)
    1218           0 :                          ? psOptions->aosLayers[i].c_str()
    1219             :                          : "0");
    1220           0 :             eErr = CE_Failure;
    1221           0 :             break;
    1222             :         }
    1223             : 
    1224          22 :         if (!psOptions->osWHERE.empty())
    1225             :         {
    1226           0 :             if (OGR_L_SetAttributeFilter(hLayer, psOptions->osWHERE.c_str()) !=
    1227             :                 OGRERR_NONE)
    1228             :             {
    1229           0 :                 eErr = CE_Failure;
    1230           0 :                 break;
    1231             :             }
    1232             :         }
    1233             : 
    1234          44 :         void *pScaledProgress = GDALCreateScaledProgress(
    1235          22 :             0.0, 1.0 * (i + 1) / nLayerCount, psOptions->pfnProgress,
    1236          22 :             psOptions->pProgressData);
    1237             : 
    1238          66 :         eErr = ProcessLayer(hLayer, !psOptions->oOutputSRS.IsEmpty(), hDstDS,
    1239          22 :                             psOptions->anBandList, psOptions->adfBurnValues,
    1240          22 :                             psOptions->b3D, psOptions->bInverse,
    1241          22 :                             psOptions->osBurnAttribute.c_str(),
    1242          22 :                             psOptions->aosRasterizeOptions, psOptions->aosTO,
    1243             :                             GDALScaledProgress, pScaledProgress);
    1244             : 
    1245          22 :         GDALDestroyScaledProgress(pScaledProgress);
    1246          22 :         if (eErr != CE_None)
    1247           0 :             break;
    1248             :     }
    1249             : 
    1250          24 :     GDALRasterizeOptionsFree(psOptionsToFree);
    1251             : 
    1252          24 :     if (eErr != CE_None)
    1253             :     {
    1254           0 :         if (bCloseOutDSOnError)
    1255           0 :             GDALClose(hDstDS);
    1256           0 :         return nullptr;
    1257             :     }
    1258             : 
    1259          24 :     return hDstDS;
    1260             : }
    1261             : 
    1262             : /************************************************************************/
    1263             : /*                       ArgIsNumericRasterize()                        */
    1264             : /************************************************************************/
    1265             : 
    1266          91 : static bool ArgIsNumericRasterize(const char *pszArg)
    1267             : 
    1268             : {
    1269          91 :     char *pszEnd = nullptr;
    1270          91 :     CPLStrtod(pszArg, &pszEnd);
    1271          91 :     return pszEnd != nullptr && pszEnd[0] == '\0';
    1272             : }
    1273             : 
    1274             : /************************************************************************/
    1275             : /*                           GDALRasterizeOptionsNew()                  */
    1276             : /************************************************************************/
    1277             : 
    1278             : /**
    1279             :  * Allocates a GDALRasterizeOptions struct.
    1280             :  *
    1281             :  * @param papszArgv NULL terminated list of options (potentially including
    1282             :  * filename and open options too), or NULL. The accepted options are the ones of
    1283             :  * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
    1284             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    1285             :  * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
    1286             :  *                           GDALRasterizeOptionsForBinaryNew() prior to this
    1287             :  * function. Will be filled with potentially present filename, open options,...
    1288             :  * @return pointer to the allocated GDALRasterizeOptions struct. Must be freed
    1289             :  * with GDALRasterizeOptionsFree().
    1290             :  *
    1291             :  * @since GDAL 2.1
    1292             :  */
    1293             : 
    1294             : GDALRasterizeOptions *
    1295          29 : GDALRasterizeOptionsNew(char **papszArgv,
    1296             :                         GDALRasterizeOptionsForBinary *psOptionsForBinary)
    1297             : {
    1298             : 
    1299          58 :     auto psOptions = std::make_unique<GDALRasterizeOptions>();
    1300             : 
    1301             :     /*-------------------------------------------------------------------- */
    1302             :     /*      Parse arguments.                                               */
    1303             :     /*-------------------------------------------------------------------- */
    1304             : 
    1305          58 :     CPLStringList aosArgv;
    1306             : 
    1307             :     /* -------------------------------------------------------------------- */
    1308             :     /*      Pre-processing for custom syntax that ArgumentParser does not   */
    1309             :     /*      support.                                                        */
    1310             :     /* -------------------------------------------------------------------- */
    1311          29 :     const int argc = CSLCount(papszArgv);
    1312         222 :     for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
    1313             :          i++)
    1314             :     {
    1315             :         // argparser will be confused if the value of a string argument
    1316             :         // starts with a negative sign.
    1317         193 :         if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
    1318             :         {
    1319           4 :             ++i;
    1320           4 :             const std::string s = papszArgv[i];
    1321           4 :             psOptions->osNoData = s;
    1322           4 :             psOptions->bCreateOutput = true;
    1323             :         }
    1324             : 
    1325             :         // argparser is confused by arguments that have at_least_one
    1326             :         // cardinality, if they immediately precede positional arguments.
    1327         189 :         else if (EQUAL(papszArgv[i], "-burn") && papszArgv[i + 1])
    1328             :         {
    1329          29 :             if (strchr(papszArgv[i + 1], ' '))
    1330             :             {
    1331             :                 const CPLStringList aosTokens(
    1332           0 :                     CSLTokenizeString(papszArgv[i + 1]));
    1333           0 :                 for (const char *pszToken : aosTokens)
    1334             :                 {
    1335           0 :                     psOptions->adfBurnValues.push_back(CPLAtof(pszToken));
    1336             :                 }
    1337           0 :                 i += 1;
    1338             :             }
    1339             :             else
    1340             :             {
    1341          58 :                 while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
    1342             :                 {
    1343          29 :                     psOptions->adfBurnValues.push_back(
    1344          29 :                         CPLAtof(papszArgv[i + 1]));
    1345          29 :                     i += 1;
    1346             :                 }
    1347             :             }
    1348             : 
    1349             :             // Dummy value to make argparse happy, as at least one of
    1350             :             // -burn, -a or -3d is required
    1351          29 :             aosArgv.AddString("-burn");
    1352          29 :             aosArgv.AddString("0");
    1353             :         }
    1354         160 :         else if (EQUAL(papszArgv[i], "-init") && papszArgv[i + 1])
    1355             :         {
    1356           3 :             if (strchr(papszArgv[i + 1], ' '))
    1357             :             {
    1358             :                 const CPLStringList aosTokens(
    1359           0 :                     CSLTokenizeString(papszArgv[i + 1]));
    1360           0 :                 for (const char *pszToken : aosTokens)
    1361             :                 {
    1362           0 :                     psOptions->adfInitVals.push_back(CPLAtof(pszToken));
    1363             :                 }
    1364           0 :                 i += 1;
    1365             :             }
    1366             :             else
    1367             :             {
    1368           6 :                 while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
    1369             :                 {
    1370           3 :                     psOptions->adfInitVals.push_back(CPLAtof(papszArgv[i + 1]));
    1371           3 :                     i += 1;
    1372             :                 }
    1373             :             }
    1374           3 :             psOptions->bCreateOutput = true;
    1375             :         }
    1376         157 :         else if (EQUAL(papszArgv[i], "-b") && papszArgv[i + 1])
    1377             :         {
    1378          18 :             if (strchr(papszArgv[i + 1], ' '))
    1379             :             {
    1380             :                 const CPLStringList aosTokens(
    1381           0 :                     CSLTokenizeString(papszArgv[i + 1]));
    1382           0 :                 for (const char *pszToken : aosTokens)
    1383             :                 {
    1384           0 :                     psOptions->anBandList.push_back(atoi(pszToken));
    1385             :                 }
    1386           0 :                 i += 1;
    1387             :             }
    1388             :             else
    1389             :             {
    1390          36 :                 while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
    1391             :                 {
    1392          18 :                     psOptions->anBandList.push_back(atoi(papszArgv[i + 1]));
    1393          18 :                     i += 1;
    1394             :                 }
    1395          18 :             }
    1396             :         }
    1397             :         else
    1398             :         {
    1399         139 :             aosArgv.AddString(papszArgv[i]);
    1400             :         }
    1401             :     }
    1402             : 
    1403             :     try
    1404             :     {
    1405             :         auto argParser =
    1406          29 :             GDALRasterizeOptionsGetParser(psOptions.get(), psOptionsForBinary);
    1407          29 :         argParser->parse_args_without_binary_name(aosArgv.List());
    1408             : 
    1409             :         // Check all no store_into args
    1410          31 :         if (auto oTe = argParser->present<std::vector<double>>("-te"))
    1411             :         {
    1412           2 :             psOptions->sEnvelop.MinX = oTe.value()[0];
    1413           2 :             psOptions->sEnvelop.MinY = oTe.value()[1];
    1414           2 :             psOptions->sEnvelop.MaxX = oTe.value()[2];
    1415           2 :             psOptions->sEnvelop.MaxY = oTe.value()[3];
    1416           2 :             psOptions->bCreateOutput = true;
    1417             :         }
    1418             : 
    1419          29 :         if (auto oTr = argParser->present<std::vector<double>>("-tr"))
    1420             :         {
    1421           7 :             psOptions->dfXRes = oTr.value()[0];
    1422           7 :             psOptions->dfYRes = oTr.value()[1];
    1423             : 
    1424           7 :             if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
    1425             :             {
    1426           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1427             :                          "Wrong value for -tr parameter.");
    1428           0 :                 return nullptr;
    1429             :             }
    1430             : 
    1431           7 :             psOptions->bCreateOutput = true;
    1432             :         }
    1433             : 
    1434          29 :         if (auto oTs = argParser->present<std::vector<int>>("-ts"))
    1435             :         {
    1436           5 :             psOptions->nXSize = oTs.value()[0];
    1437           5 :             psOptions->nYSize = oTs.value()[1];
    1438             : 
    1439           5 :             if (psOptions->nXSize <= 0 || psOptions->nYSize <= 0)
    1440             :             {
    1441           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1442             :                          "Wrong value for -ts parameter.");
    1443           0 :                 return nullptr;
    1444             :             }
    1445             : 
    1446           5 :             psOptions->bCreateOutput = true;
    1447             :         }
    1448             : 
    1449          29 :         if (psOptions->bCreateOutput)
    1450             :         {
    1451          17 :             if (psOptions->dfXRes == 0 && psOptions->dfYRes == 0 &&
    1452          17 :                 psOptions->nXSize == 0 && psOptions->nYSize == 0)
    1453             :             {
    1454           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1455             :                          "'-tr xres yres' or '-ts xsize ysize' is required.");
    1456           0 :                 return nullptr;
    1457             :             }
    1458             : 
    1459          12 :             if (psOptions->bTargetAlignedPixels && psOptions->dfXRes == 0 &&
    1460           0 :                 psOptions->dfYRes == 0)
    1461             :             {
    1462           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1463             :                          "-tap option cannot be used without using -tr.");
    1464           0 :                 return nullptr;
    1465             :             }
    1466             : 
    1467          12 :             if (!psOptions->anBandList.empty())
    1468             :             {
    1469           0 :                 CPLError(
    1470             :                     CE_Failure, CPLE_NotSupported,
    1471             :                     "-b option cannot be used when creating a GDAL dataset.");
    1472           0 :                 return nullptr;
    1473             :             }
    1474             : 
    1475          12 :             int nBandCount = 1;
    1476             : 
    1477          12 :             if (!psOptions->adfBurnValues.empty())
    1478           3 :                 nBandCount = static_cast<int>(psOptions->adfBurnValues.size());
    1479             : 
    1480          12 :             if (static_cast<int>(psOptions->adfInitVals.size()) > nBandCount)
    1481           0 :                 nBandCount = static_cast<int>(psOptions->adfInitVals.size());
    1482             : 
    1483          12 :             if (psOptions->adfInitVals.size() == 1)
    1484             :             {
    1485           3 :                 for (int i = 1; i <= nBandCount - 1; i++)
    1486           0 :                     psOptions->adfInitVals.push_back(psOptions->adfInitVals[0]);
    1487             :             }
    1488             : 
    1489          24 :             for (int i = 1; i <= nBandCount; i++)
    1490          12 :                 psOptions->anBandList.push_back(i);
    1491             :         }
    1492             :         else
    1493             :         {
    1494          17 :             if (psOptions->anBandList.empty())
    1495          11 :                 psOptions->anBandList.push_back(1);
    1496             :         }
    1497             : 
    1498          29 :         if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
    1499           0 :             !psOptions->osSQL.empty())
    1500             :         {
    1501           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1502             :                      "-dialect is ignored with -where. Use -sql instead");
    1503             :         }
    1504             : 
    1505          29 :         if (psOptionsForBinary)
    1506             :         {
    1507           9 :             psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
    1508           9 :             if (!psOptions->osFormat.empty())
    1509           0 :                 psOptionsForBinary->osFormat = psOptions->osFormat;
    1510             :         }
    1511          28 :         else if (psOptions->adfBurnValues.empty() &&
    1512          28 :                  psOptions->osBurnAttribute.empty() && !psOptions->b3D)
    1513             :         {
    1514           6 :             psOptions->adfBurnValues.push_back(255);
    1515             :         }
    1516             :     }
    1517           0 :     catch (const std::exception &e)
    1518             :     {
    1519           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1520           0 :         return nullptr;
    1521             :     }
    1522             : 
    1523          29 :     return psOptions.release();
    1524             : }
    1525             : 
    1526             : /************************************************************************/
    1527             : /*                       GDALRasterizeOptionsFree()                     */
    1528             : /************************************************************************/
    1529             : 
    1530             : /**
    1531             :  * Frees the GDALRasterizeOptions struct.
    1532             :  *
    1533             :  * @param psOptions the options struct for GDALRasterize().
    1534             :  *
    1535             :  * @since GDAL 2.1
    1536             :  */
    1537             : 
    1538          58 : void GDALRasterizeOptionsFree(GDALRasterizeOptions *psOptions)
    1539             : {
    1540          58 :     delete psOptions;
    1541          58 : }
    1542             : 
    1543             : /************************************************************************/
    1544             : /*                  GDALRasterizeOptionsSetProgress()                   */
    1545             : /************************************************************************/
    1546             : 
    1547             : /**
    1548             :  * Set a progress function.
    1549             :  *
    1550             :  * @param psOptions the options struct for GDALRasterize().
    1551             :  * @param pfnProgress the progress callback.
    1552             :  * @param pProgressData the user data for the progress callback.
    1553             :  *
    1554             :  * @since GDAL 2.1
    1555             :  */
    1556             : 
    1557           8 : void GDALRasterizeOptionsSetProgress(GDALRasterizeOptions *psOptions,
    1558             :                                      GDALProgressFunc pfnProgress,
    1559             :                                      void *pProgressData)
    1560             : {
    1561           8 :     psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
    1562           8 :     psOptions->pProgressData = pProgressData;
    1563           8 : }

Generated by: LCOV version 1.14