Coverage Report

Created: 2026-02-14 06:52

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