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

Generated by: LCOV version 1.14