Coverage Report

Created: 2026-05-16 08:20

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
        CPLStringList aosTransformerOptions(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
            aosTransformerOptions.SetNameValue("DST_METHOD", "NO_GEOTRANSFORM");
780
0
        }
781
782
0
        pTransformArg = GDALCreateGenImgProjTransformer2(
783
0
            nullptr, hDstDS, aosTransformerOptions.List());
784
785
0
        pfnTransformer = GDALGenImgProjTransform;
786
0
        if (pTransformArg == nullptr)
787
0
        {
788
0
            eErr = CE_Failure;
789
0
        }
790
0
    }
791
792
    /* -------------------------------------------------------------------- */
793
    /*      Perform the burn.                                               */
794
    /* -------------------------------------------------------------------- */
795
0
    if (eErr == CE_None)
796
0
    {
797
0
        if (bUseInt64)
798
0
        {
799
0
            eErr = GDALRasterizeGeometriesInt64(
800
0
                hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
801
0
                static_cast<int>(ahGeometries.size()), ahGeometries.data(),
802
0
                pfnTransformer, pTransformArg, anFullBurnValues.data(),
803
0
                papszRasterizeOptions, pfnProgress, pProgressData);
804
0
        }
805
0
        else
806
0
        {
807
0
            eErr = GDALRasterizeGeometries(
808
0
                hDstDS, static_cast<int>(anBandList.size()), anBandList.data(),
809
0
                static_cast<int>(ahGeometries.size()), ahGeometries.data(),
810
0
                pfnTransformer, pTransformArg, adfFullBurnValues.data(),
811
0
                papszRasterizeOptions, pfnProgress, pProgressData);
812
0
        }
813
0
    }
814
815
    /* -------------------------------------------------------------------- */
816
    /*      Cleanup                                                         */
817
    /* -------------------------------------------------------------------- */
818
819
0
    if (pTransformArg)
820
0
        GDALDestroyTransformer(pTransformArg);
821
822
0
    for (int iGeom = static_cast<int>(ahGeometries.size()) - 1; iGeom >= 0;
823
0
         iGeom--)
824
0
        OGR_G_DestroyGeometry(ahGeometries[iGeom]);
825
826
0
    return eErr;
827
0
}
828
829
/************************************************************************/
830
/*                        CreateOutputDataset()                         */
831
/************************************************************************/
832
833
static std::unique_ptr<GDALDataset> CreateOutputDataset(
834
    const std::vector<OGRLayerH> &ahLayers, OGRSpatialReferenceH hSRS,
835
    OGREnvelope sEnvelop, GDALDriverH hDriver, const char *pszDest, int nXSize,
836
    int nYSize, double dfXRes, double dfYRes, bool bTargetAlignedPixels,
837
    int nBandCount, GDALDataType eOutputType, CSLConstList papszCreationOptions,
838
    const std::vector<double> &adfInitVals, const char *pszNoData)
839
0
{
840
0
    bool bFirstLayer = true;
841
0
    const bool bBoundsSpecifiedByUser = CPL_TO_BOOL(sEnvelop.IsInit());
842
843
0
    for (unsigned int i = 0; i < ahLayers.size(); i++)
844
0
    {
845
0
        OGRLayerH hLayer = ahLayers[i];
846
847
0
        if (!bBoundsSpecifiedByUser)
848
0
        {
849
0
            OGREnvelope sLayerEnvelop;
850
851
0
            if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
852
0
            {
853
0
                CPLError(CE_Failure, CPLE_AppDefined,
854
0
                         "Cannot get layer extent");
855
0
                return nullptr;
856
0
            }
857
858
            /* Voluntarily increase the extent by a half-pixel size to avoid */
859
            /* missing points on the border */
860
0
            if (!bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
861
0
            {
862
0
                sLayerEnvelop.MinX -= dfXRes / 2;
863
0
                sLayerEnvelop.MaxX += dfXRes / 2;
864
0
                sLayerEnvelop.MinY -= dfYRes / 2;
865
0
                sLayerEnvelop.MaxY += dfYRes / 2;
866
0
            }
867
868
0
            sEnvelop.Merge(sLayerEnvelop);
869
0
        }
870
871
0
        if (bFirstLayer)
872
0
        {
873
0
            if (hSRS == nullptr)
874
0
                hSRS = OGR_L_GetSpatialRef(hLayer);
875
876
0
            bFirstLayer = false;
877
0
        }
878
0
    }
879
880
0
    if (!sEnvelop.IsInit())
881
0
    {
882
0
        CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
883
0
        return nullptr;
884
0
    }
885
886
0
    if (dfXRes == 0 && dfYRes == 0)
887
0
    {
888
0
        if (nXSize == 0 || nYSize == 0)
889
0
        {
890
0
            CPLError(CE_Failure, CPLE_AppDefined,
891
0
                     "Size and resolution are missing");
892
0
            return nullptr;
893
0
        }
894
0
        dfXRes = (sEnvelop.MaxX - sEnvelop.MinX) / nXSize;
895
0
        dfYRes = (sEnvelop.MaxY - sEnvelop.MinY) / nYSize;
896
0
    }
897
0
    else if (bTargetAlignedPixels && dfXRes != 0 && dfYRes != 0)
898
0
    {
899
0
        sEnvelop.MinX = floor(sEnvelop.MinX / dfXRes) * dfXRes;
900
0
        sEnvelop.MaxX = ceil(sEnvelop.MaxX / dfXRes) * dfXRes;
901
0
        sEnvelop.MinY = floor(sEnvelop.MinY / dfYRes) * dfYRes;
902
0
        sEnvelop.MaxY = ceil(sEnvelop.MaxY / dfYRes) * dfYRes;
903
0
    }
904
905
0
    if (dfXRes == 0 || dfYRes == 0)
906
0
    {
907
0
        CPLError(CE_Failure, CPLE_AppDefined, "Could not determine bounds");
908
0
        return nullptr;
909
0
    }
910
911
0
    if (nXSize == 0 && nYSize == 0)
912
0
    {
913
        // coverity[divide_by_zero]
914
0
        const double dfXSize = 0.5 + (sEnvelop.MaxX - sEnvelop.MinX) / dfXRes;
915
        // coverity[divide_by_zero]
916
0
        const double dfYSize = 0.5 + (sEnvelop.MaxY - sEnvelop.MinY) / dfYRes;
917
0
        if (dfXSize > std::numeric_limits<int>::max() ||
918
0
            dfXSize < std::numeric_limits<int>::min() ||
919
0
            dfYSize > std::numeric_limits<int>::max() ||
920
0
            dfYSize < std::numeric_limits<int>::min())
921
0
        {
922
0
            CPLError(CE_Failure, CPLE_AppDefined,
923
0
                     "Invalid computed output raster size: %f x %f", dfXSize,
924
0
                     dfYSize);
925
0
            return nullptr;
926
0
        }
927
0
        nXSize = static_cast<int>(dfXSize);
928
0
        nYSize = static_cast<int>(dfYSize);
929
0
    }
930
931
0
    auto poDstDS =
932
0
        std::unique_ptr<GDALDataset>(GDALDriver::FromHandle(hDriver)->Create(
933
0
            pszDest, nXSize, nYSize, nBandCount, eOutputType,
934
0
            papszCreationOptions));
935
0
    if (poDstDS == nullptr)
936
0
    {
937
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszDest);
938
0
        return nullptr;
939
0
    }
940
941
0
    GDALGeoTransform gt = {sEnvelop.MinX, dfXRes, 0.0,
942
0
                           sEnvelop.MaxY, 0.0,    -dfYRes};
943
0
    poDstDS->SetGeoTransform(gt);
944
945
0
    if (hSRS)
946
0
        poDstDS->SetSpatialRef(OGRSpatialReference::FromHandle(hSRS));
947
948
0
    if (pszNoData)
949
0
    {
950
0
        for (int iBand = 0; iBand < nBandCount; iBand++)
951
0
        {
952
0
            auto poBand = poDstDS->GetRasterBand(iBand + 1);
953
0
            if (poBand->GetRasterDataType() == GDT_Int64)
954
0
                poBand->SetNoDataValueAsInt64(CPLAtoGIntBig(pszNoData));
955
0
            else
956
0
                poBand->SetNoDataValue(CPLAtof(pszNoData));
957
0
        }
958
0
    }
959
960
0
    if (!adfInitVals.empty())
961
0
    {
962
0
        for (int iBand = 0;
963
0
             iBand < std::min(nBandCount, static_cast<int>(adfInitVals.size()));
964
0
             iBand++)
965
0
        {
966
0
            auto poBand = poDstDS->GetRasterBand(iBand + 1);
967
0
            poBand->Fill(adfInitVals[iBand]);
968
0
        }
969
0
    }
970
971
0
    return poDstDS;
972
0
}
973
974
/************************************************************************/
975
/*                           GDALRasterize()                            */
976
/************************************************************************/
977
978
/* clang-format off */
979
/**
980
 * Burns vector geometries into a raster
981
 *
982
 * This is the equivalent of the
983
 * <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
984
 *
985
 * GDALRasterizeOptions* must be allocated and freed with
986
 * GDALRasterizeOptionsNew() and GDALRasterizeOptionsFree() respectively.
987
 * pszDest and hDstDS cannot be used at the same time.
988
 *
989
 * @param pszDest the destination dataset path or NULL.
990
 * @param hDstDS the destination dataset or NULL.
991
 * @param hSrcDataset the source dataset handle.
992
 * @param psOptionsIn the options struct returned by GDALRasterizeOptionsNew()
993
 * or NULL.
994
 * @param pbUsageError pointer to an integer output variable to store if any
995
 * usage error has occurred or NULL.
996
 * @return the output dataset (new dataset that must be closed using
997
 * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
998
 *
999
 * @since GDAL 2.1
1000
 */
1001
/* clang-format on */
1002
1003
GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS,
1004
                           GDALDatasetH hSrcDataset,
1005
                           const GDALRasterizeOptions *psOptionsIn,
1006
                           int *pbUsageError)
1007
0
{
1008
0
    GDALDataset *poOutDS = GDALDataset::FromHandle(hDstDS);
1009
0
#define hDstDS no_longer_use_hDstDS
1010
0
    if (pszDest == nullptr && poOutDS == nullptr)
1011
0
    {
1012
0
        CPLError(CE_Failure, CPLE_AppDefined,
1013
0
                 "pszDest == NULL && hDstDS == NULL");
1014
1015
0
        if (pbUsageError)
1016
0
            *pbUsageError = TRUE;
1017
0
        return nullptr;
1018
0
    }
1019
0
    if (hSrcDataset == nullptr)
1020
0
    {
1021
0
        CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
1022
1023
0
        if (pbUsageError)
1024
0
            *pbUsageError = TRUE;
1025
0
        return nullptr;
1026
0
    }
1027
0
    if (poOutDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
1028
0
    {
1029
0
        CPLError(CE_Failure, CPLE_AppDefined,
1030
0
                 "hDstDS != NULL but options that imply creating a new dataset "
1031
0
                 "have been set.");
1032
1033
0
        if (pbUsageError)
1034
0
            *pbUsageError = TRUE;
1035
0
        return nullptr;
1036
0
    }
1037
1038
0
    std::unique_ptr<GDALRasterizeOptions, decltype(&GDALRasterizeOptionsFree)>
1039
0
        psOptionsToFree(nullptr, GDALRasterizeOptionsFree);
1040
0
    GDALRasterizeOptions sOptions;
1041
0
    if (psOptionsIn)
1042
0
        sOptions = *psOptionsIn;
1043
1044
0
    std::unique_ptr<GDALDataset> poNewOutDS;
1045
0
    if (pszDest == nullptr)
1046
0
        pszDest = poOutDS->GetDescription();
1047
1048
0
    if (sOptions.osSQL.empty() && sOptions.aosLayers.empty() &&
1049
0
        GDALDatasetGetLayerCount(hSrcDataset) != 1)
1050
0
    {
1051
0
        CPLError(CE_Failure, CPLE_NotSupported,
1052
0
                 "Neither -sql nor -l are specified, but the source dataset "
1053
0
                 "has not one single layer.");
1054
0
        if (pbUsageError)
1055
0
            *pbUsageError = TRUE;
1056
0
        return nullptr;
1057
0
    }
1058
1059
    /* -------------------------------------------------------------------- */
1060
    /*      Open target raster file.  Eventually we will add optional       */
1061
    /*      creation.                                                       */
1062
    /* -------------------------------------------------------------------- */
1063
0
    const bool bCreateOutput = sOptions.bCreateOutput || poOutDS == nullptr;
1064
1065
0
    GDALDriverH hDriver = nullptr;
1066
0
    if (bCreateOutput)
1067
0
    {
1068
0
        CPLString osFormat;
1069
0
        if (sOptions.osFormat.empty())
1070
0
        {
1071
0
            osFormat = GetOutputDriverForRaster(pszDest);
1072
0
            if (osFormat.empty())
1073
0
            {
1074
0
                return nullptr;
1075
0
            }
1076
0
        }
1077
0
        else
1078
0
        {
1079
0
            osFormat = sOptions.osFormat;
1080
0
        }
1081
1082
        /* ------------------------------------------------------------------ */
1083
        /*      Find the output driver. */
1084
        /* ------------------------------------------------------------------ */
1085
0
        hDriver = GDALGetDriverByName(osFormat);
1086
0
        CSLConstList papszDriverMD =
1087
0
            hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
1088
0
        if (hDriver == nullptr)
1089
0
        {
1090
0
            CPLError(CE_Failure, CPLE_NotSupported,
1091
0
                     "Output driver `%s' not recognised.", osFormat.c_str());
1092
0
            return nullptr;
1093
0
        }
1094
0
        if (!CPLTestBool(
1095
0
                CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, "FALSE")))
1096
0
        {
1097
0
            CPLError(CE_Failure, CPLE_NotSupported,
1098
0
                     "Output driver `%s' is not a raster driver.",
1099
0
                     osFormat.c_str());
1100
0
            return nullptr;
1101
0
        }
1102
0
        if (!CPLTestBool(
1103
0
                CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
1104
0
        {
1105
0
            CPLError(CE_Failure, CPLE_NotSupported,
1106
0
                     "Output driver `%s' does not support direct output file "
1107
0
                     "creation. "
1108
0
                     "To write a file to this format, first write to a "
1109
0
                     "different format such as "
1110
0
                     "GeoTIFF and then convert the output.",
1111
0
                     osFormat.c_str());
1112
0
            return nullptr;
1113
0
        }
1114
0
    }
1115
1116
0
    auto calculateSize = [&](const OGREnvelope &sEnvelope) -> bool
1117
0
    {
1118
0
        const double width{sEnvelope.MaxX - sEnvelope.MinX};
1119
0
        if (std::isnan(width))
1120
0
        {
1121
0
            return false;
1122
0
        }
1123
1124
0
        const double height{sEnvelope.MaxY - sEnvelope.MinY};
1125
0
        if (std::isnan(height))
1126
0
        {
1127
0
            return false;
1128
0
        }
1129
1130
0
        if (height == 0 || width == 0)
1131
0
        {
1132
0
            return false;
1133
0
        }
1134
1135
0
        if (sOptions.nXSize == 0)
1136
0
        {
1137
0
            const double xSize{
1138
0
                (sEnvelope.MaxX - sEnvelope.MinX) /
1139
0
                ((sEnvelope.MaxY - sEnvelope.MinY) / sOptions.nYSize)};
1140
0
            if (std::isnan(xSize) || xSize > std::numeric_limits<int>::max() ||
1141
0
                xSize < std::numeric_limits<int>::min())
1142
0
            {
1143
0
                return false;
1144
0
            }
1145
0
            sOptions.nXSize = static_cast<int>(xSize);
1146
0
        }
1147
0
        else
1148
0
        {
1149
0
            const double ySize{
1150
0
                (sEnvelope.MaxY - sEnvelope.MinY) /
1151
0
                ((sEnvelope.MaxX - sEnvelope.MinX) / sOptions.nXSize)};
1152
0
            if (std::isnan(ySize) || ySize > std::numeric_limits<int>::max() ||
1153
0
                ySize < std::numeric_limits<int>::min())
1154
0
            {
1155
0
                return false;
1156
0
            }
1157
0
            sOptions.nYSize = static_cast<int>(ySize);
1158
0
        }
1159
0
        return sOptions.nXSize > 0 && sOptions.nYSize > 0;
1160
0
    };
1161
1162
0
    const int nLayerCount =
1163
0
        (sOptions.osSQL.empty() && sOptions.aosLayers.empty())
1164
0
            ? 1
1165
0
            : static_cast<int>(sOptions.aosLayers.size());
1166
1167
0
    const bool bOneSizeNeedsCalculation{
1168
0
        static_cast<bool>((sOptions.nXSize == 0) ^ (sOptions.nYSize == 0))};
1169
1170
    // Calculate the size if either nXSize or nYSize is 0
1171
0
    if (sOptions.osSQL.empty() && bOneSizeNeedsCalculation)
1172
0
    {
1173
0
        CPLErr eErr = CE_None;
1174
        // Get the extent of the source dataset
1175
0
        OGREnvelope sEnvelope;
1176
0
        bool bFirstLayer = true;
1177
0
        for (int i = 0; i < nLayerCount; i++)
1178
0
        {
1179
0
            OGRLayerH hLayer;
1180
0
            if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1181
0
                hLayer = GDALDatasetGetLayerByName(
1182
0
                    hSrcDataset, sOptions.aosLayers[i].c_str());
1183
0
            else
1184
0
                hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1185
0
            if (hLayer == nullptr)
1186
0
            {
1187
0
                CPLError(CE_Failure, CPLE_AppDefined,
1188
0
                         "Unable to find layer \"%s\".",
1189
0
                         sOptions.aosLayers.size() > static_cast<size_t>(i)
1190
0
                             ? sOptions.aosLayers[i].c_str()
1191
0
                             : "0");
1192
0
                eErr = CE_Failure;
1193
0
                break;
1194
0
            }
1195
0
            OGREnvelope sLayerEnvelop;
1196
0
            if (OGR_L_GetExtent(hLayer, &sLayerEnvelop, TRUE) != OGRERR_NONE)
1197
0
            {
1198
0
                CPLError(CE_Failure, CPLE_AppDefined,
1199
0
                         "Cannot get layer extent");
1200
0
                eErr = CE_Failure;
1201
0
                break;
1202
0
            }
1203
0
            if (bFirstLayer)
1204
0
            {
1205
0
                sEnvelope = sLayerEnvelop;
1206
0
                bFirstLayer = false;
1207
0
            }
1208
0
            else
1209
0
            {
1210
0
                sEnvelope.Merge(sLayerEnvelop);
1211
0
            }
1212
0
        }
1213
1214
0
        if (!calculateSize(sEnvelope))
1215
0
        {
1216
0
            CPLError(CE_Failure, CPLE_AppDefined,
1217
0
                     "Cannot calculate size from layer extent");
1218
0
            eErr = CE_Failure;
1219
0
        }
1220
1221
0
        if (eErr == CE_Failure)
1222
0
        {
1223
0
            return nullptr;
1224
0
        }
1225
0
    }
1226
1227
0
    const auto GetOutputDataType = [&](OGRLayerH hLayer)
1228
0
    {
1229
0
        CPLAssert(bCreateOutput);
1230
0
        CPLAssert(hDriver);
1231
0
        GDALDataType eOutputType = sOptions.eOutputType;
1232
0
        if (eOutputType == GDT_Unknown && !sOptions.osBurnAttribute.empty())
1233
0
        {
1234
0
            OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer);
1235
0
            const int iBurnField = OGR_FD_GetFieldIndex(
1236
0
                hLayerDefn, sOptions.osBurnAttribute.c_str());
1237
0
            if (iBurnField >= 0 && OGR_Fld_GetType(OGR_FD_GetFieldDefn(
1238
0
                                       hLayerDefn, iBurnField)) == OFTInteger64)
1239
0
            {
1240
0
                const char *pszMD = GDALGetMetadataItem(
1241
0
                    hDriver, GDAL_DMD_CREATIONDATATYPES, nullptr);
1242
0
                if (pszMD && CPLStringList(CSLTokenizeString2(pszMD, " ", 0))
1243
0
                                     .FindString("Int64") >= 0)
1244
0
                {
1245
0
                    eOutputType = GDT_Int64;
1246
0
                }
1247
0
            }
1248
0
        }
1249
0
        if (eOutputType == GDT_Unknown)
1250
0
        {
1251
0
            eOutputType = GDT_Float64;
1252
0
        }
1253
0
        return eOutputType;
1254
0
    };
1255
1256
    // Store SRS handle
1257
0
    OGRSpatialReferenceH hSRS =
1258
0
        sOptions.oOutputSRS.IsEmpty()
1259
0
            ? nullptr
1260
0
            : OGRSpatialReference::ToHandle(
1261
0
                  const_cast<OGRSpatialReference *>(&sOptions.oOutputSRS));
1262
1263
    /* -------------------------------------------------------------------- */
1264
    /*      Process SQL request.                                            */
1265
    /* -------------------------------------------------------------------- */
1266
0
    CPLErr eErr = CE_Failure;
1267
1268
0
    if (!sOptions.osSQL.empty())
1269
0
    {
1270
0
        OGRLayerH hLayer =
1271
0
            GDALDatasetExecuteSQL(hSrcDataset, sOptions.osSQL.c_str(), nullptr,
1272
0
                                  sOptions.osDialect.c_str());
1273
0
        if (hLayer != nullptr)
1274
0
        {
1275
1276
0
            if (bOneSizeNeedsCalculation)
1277
0
            {
1278
0
                OGREnvelope sEnvelope;
1279
0
                bool bSizeCalculationError{
1280
0
                    OGR_L_GetExtent(hLayer, &sEnvelope, TRUE) != OGRERR_NONE};
1281
0
                if (!bSizeCalculationError)
1282
0
                {
1283
0
                    bSizeCalculationError = !calculateSize(sEnvelope);
1284
0
                }
1285
1286
0
                if (bSizeCalculationError)
1287
0
                {
1288
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1289
0
                             "Cannot get layer extent");
1290
0
                    GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1291
0
                    return nullptr;
1292
0
                }
1293
0
            }
1294
1295
0
            if (bCreateOutput)
1296
0
            {
1297
0
                std::vector<OGRLayerH> ahLayers;
1298
0
                ahLayers.push_back(hLayer);
1299
1300
0
                const GDALDataType eOutputType = GetOutputDataType(hLayer);
1301
0
                poNewOutDS = CreateOutputDataset(
1302
0
                    ahLayers, hSRS, sOptions.sEnvelop, hDriver, pszDest,
1303
0
                    sOptions.nXSize, sOptions.nYSize, sOptions.dfXRes,
1304
0
                    sOptions.dfYRes, sOptions.bTargetAlignedPixels,
1305
0
                    static_cast<int>(sOptions.anBandList.size()), eOutputType,
1306
0
                    sOptions.aosCreationOptions, sOptions.adfInitVals,
1307
0
                    sOptions.osNoData.c_str());
1308
0
                if (poNewOutDS == nullptr)
1309
0
                {
1310
0
                    GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1311
0
                    return nullptr;
1312
0
                }
1313
0
                poOutDS = poNewOutDS.get();
1314
0
            }
1315
1316
0
            const bool bCloseReportsProgress =
1317
0
                bCreateOutput && poOutDS->GetCloseReportsProgress();
1318
1319
0
            std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1320
0
                pScaledProgressArg(GDALCreateScaledProgress(
1321
0
                                       0.0, bCloseReportsProgress ? 0.5 : 1.0,
1322
0
                                       sOptions.pfnProgress,
1323
0
                                       sOptions.pProgressData),
1324
0
                                   GDALDestroyScaledProgress);
1325
1326
0
            eErr = ProcessLayer(
1327
0
                hLayer, hSRS != nullptr, poOutDS, sOptions.anBandList,
1328
0
                sOptions.adfBurnValues, sOptions.b3D, sOptions.bInverse,
1329
0
                sOptions.osBurnAttribute.c_str(), sOptions.aosRasterizeOptions,
1330
0
                sOptions.aosTO, GDALScaledProgress, pScaledProgressArg.get());
1331
1332
0
            GDALDatasetReleaseResultSet(hSrcDataset, hLayer);
1333
0
        }
1334
0
    }
1335
1336
    /* -------------------------------------------------------------------- */
1337
    /*      Create output file if necessary.                                */
1338
    /* -------------------------------------------------------------------- */
1339
1340
0
    if (bCreateOutput && poOutDS == nullptr)
1341
0
    {
1342
0
        std::vector<OGRLayerH> ahLayers;
1343
1344
0
        GDALDataType eOutputType = sOptions.eOutputType;
1345
1346
0
        for (int i = 0; i < nLayerCount; i++)
1347
0
        {
1348
0
            OGRLayerH hLayer;
1349
0
            if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1350
0
                hLayer = GDALDatasetGetLayerByName(
1351
0
                    hSrcDataset, sOptions.aosLayers[i].c_str());
1352
0
            else
1353
0
                hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1354
0
            if (hLayer == nullptr)
1355
0
            {
1356
0
                CPLError(CE_Failure, CPLE_AppDefined,
1357
0
                         "Unable to find layer \"%s\".",
1358
0
                         sOptions.aosLayers.size() > static_cast<size_t>(i)
1359
0
                             ? sOptions.aosLayers[i].c_str()
1360
0
                             : "0");
1361
0
                return nullptr;
1362
0
            }
1363
0
            if (eOutputType == GDT_Unknown)
1364
0
            {
1365
0
                if (GetOutputDataType(hLayer) == GDT_Int64)
1366
0
                    eOutputType = GDT_Int64;
1367
0
            }
1368
1369
0
            ahLayers.push_back(hLayer);
1370
0
        }
1371
1372
0
        if (eOutputType == GDT_Unknown)
1373
0
        {
1374
0
            eOutputType = GDT_Float64;
1375
0
        }
1376
1377
0
        poNewOutDS = CreateOutputDataset(
1378
0
            ahLayers, hSRS, sOptions.sEnvelop, hDriver, pszDest,
1379
0
            sOptions.nXSize, sOptions.nYSize, sOptions.dfXRes, sOptions.dfYRes,
1380
0
            sOptions.bTargetAlignedPixels,
1381
0
            static_cast<int>(sOptions.anBandList.size()), eOutputType,
1382
0
            sOptions.aosCreationOptions, sOptions.adfInitVals,
1383
0
            sOptions.osNoData.c_str());
1384
0
        if (poNewOutDS == nullptr)
1385
0
        {
1386
0
            return nullptr;
1387
0
        }
1388
0
        poOutDS = poNewOutDS.get();
1389
0
    }
1390
1391
0
    const bool bCloseReportsProgress =
1392
0
        bCreateOutput && poOutDS->GetCloseReportsProgress();
1393
1394
    /* -------------------------------------------------------------------- */
1395
    /*      Process each layer.                                             */
1396
    /* -------------------------------------------------------------------- */
1397
1398
0
    for (int i = 0; i < nLayerCount; i++)
1399
0
    {
1400
0
        OGRLayerH hLayer;
1401
0
        if (sOptions.aosLayers.size() > static_cast<size_t>(i))
1402
0
            hLayer = GDALDatasetGetLayerByName(hSrcDataset,
1403
0
                                               sOptions.aosLayers[i].c_str());
1404
0
        else
1405
0
            hLayer = GDALDatasetGetLayer(hSrcDataset, 0);
1406
0
        if (hLayer == nullptr)
1407
0
        {
1408
0
            CPLError(CE_Failure, CPLE_AppDefined,
1409
0
                     "Unable to find layer \"%s\".",
1410
0
                     sOptions.aosLayers.size() > static_cast<size_t>(i)
1411
0
                         ? sOptions.aosLayers[i].c_str()
1412
0
                         : "0");
1413
0
            eErr = CE_Failure;
1414
0
            break;
1415
0
        }
1416
1417
0
        if (!sOptions.osWHERE.empty())
1418
0
        {
1419
0
            if (OGR_L_SetAttributeFilter(hLayer, sOptions.osWHERE.c_str()) !=
1420
0
                OGRERR_NONE)
1421
0
            {
1422
0
                eErr = CE_Failure;
1423
0
                break;
1424
0
            }
1425
0
        }
1426
1427
0
        const double dfFactor = bCloseReportsProgress ? 0.5 : 1.0;
1428
1429
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1430
0
            pScaledProgressArg(
1431
0
                GDALCreateScaledProgress(dfFactor * i / nLayerCount,
1432
0
                                         dfFactor * (i + 1) / nLayerCount,
1433
0
                                         sOptions.pfnProgress,
1434
0
                                         sOptions.pProgressData),
1435
0
                GDALDestroyScaledProgress);
1436
1437
0
        eErr = ProcessLayer(hLayer, !sOptions.oOutputSRS.IsEmpty(), poOutDS,
1438
0
                            sOptions.anBandList, sOptions.adfBurnValues,
1439
0
                            sOptions.b3D, sOptions.bInverse,
1440
0
                            sOptions.osBurnAttribute.c_str(),
1441
0
                            sOptions.aosRasterizeOptions, sOptions.aosTO,
1442
0
                            GDALScaledProgress, pScaledProgressArg.get());
1443
0
        if (eErr != CE_None)
1444
0
            break;
1445
0
    }
1446
1447
0
    if (eErr != CE_None)
1448
0
    {
1449
0
        return nullptr;
1450
0
    }
1451
1452
0
    if (bCloseReportsProgress)
1453
0
    {
1454
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1455
0
            pScaledProgressArg(GDALCreateScaledProgress(0.5, 1.0,
1456
0
                                                        sOptions.pfnProgress,
1457
0
                                                        sOptions.pProgressData),
1458
0
                               GDALDestroyScaledProgress);
1459
1460
0
        const bool bCanReopenWithCurrentDescription =
1461
0
            poOutDS->CanReopenWithCurrentDescription();
1462
1463
0
        eErr = poOutDS->Close(GDALScaledProgress, pScaledProgressArg.get());
1464
0
        poOutDS = nullptr;
1465
0
        if (eErr != CE_None)
1466
0
            return nullptr;
1467
1468
0
        if (bCanReopenWithCurrentDescription)
1469
0
        {
1470
0
            {
1471
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1472
0
                poNewOutDS.reset(
1473
0
                    GDALDataset::Open(pszDest, GDAL_OF_RASTER | GDAL_OF_UPDATE,
1474
0
                                      nullptr, nullptr, nullptr));
1475
0
            }
1476
0
            if (!poNewOutDS)
1477
0
            {
1478
0
                poNewOutDS.reset(GDALDataset::Open(
1479
0
                    pszDest, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
1480
0
                    nullptr, nullptr));
1481
0
            }
1482
0
        }
1483
0
        else
1484
0
        {
1485
0
            struct DummyDataset final : public GDALDataset
1486
0
            {
1487
0
                DummyDataset() = default;
1488
0
            };
1489
1490
0
            poNewOutDS = std::make_unique<DummyDataset>();
1491
0
        }
1492
0
    }
1493
1494
0
    return poNewOutDS ? poNewOutDS.release() : poOutDS;
1495
0
}
1496
1497
/************************************************************************/
1498
/*                       ArgIsNumericRasterize()                        */
1499
/************************************************************************/
1500
1501
static bool ArgIsNumericRasterize(const char *pszArg)
1502
1503
0
{
1504
0
    char *pszEnd = nullptr;
1505
0
    CPLStrtod(pszArg, &pszEnd);
1506
0
    return pszEnd != nullptr && pszEnd[0] == '\0';
1507
0
}
1508
1509
/************************************************************************/
1510
/*                      GDALRasterizeOptionsNew()                       */
1511
/************************************************************************/
1512
1513
/**
1514
 * Allocates a GDALRasterizeOptions struct.
1515
 *
1516
 * @param papszArgv NULL terminated list of options (potentially including
1517
 * filename and open options too), or NULL. The accepted options are the ones of
1518
 * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
1519
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1520
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1521
 *                           GDALRasterizeOptionsForBinaryNew() prior to this
1522
 * function. Will be filled with potentially present filename, open options,...
1523
 * @return pointer to the allocated GDALRasterizeOptions struct. Must be freed
1524
 * with GDALRasterizeOptionsFree().
1525
 *
1526
 * @since GDAL 2.1
1527
 */
1528
1529
GDALRasterizeOptions *
1530
GDALRasterizeOptionsNew(char **papszArgv,
1531
                        GDALRasterizeOptionsForBinary *psOptionsForBinary)
1532
0
{
1533
1534
0
    auto psOptions = std::make_unique<GDALRasterizeOptions>();
1535
1536
    /*-------------------------------------------------------------------- */
1537
    /*      Parse arguments.                                               */
1538
    /*-------------------------------------------------------------------- */
1539
1540
0
    CPLStringList aosArgv;
1541
1542
    /* -------------------------------------------------------------------- */
1543
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
1544
    /*      support.                                                        */
1545
    /* -------------------------------------------------------------------- */
1546
0
    const int argc = CSLCount(papszArgv);
1547
0
    for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
1548
0
         i++)
1549
0
    {
1550
        // argparser will be confused if the value of a string argument
1551
        // starts with a negative sign.
1552
0
        if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1])
1553
0
        {
1554
0
            ++i;
1555
0
            psOptions->osNoData = papszArgv[i];
1556
0
            psOptions->bCreateOutput = true;
1557
0
        }
1558
1559
        // argparser is confused by arguments that have at_least_one
1560
        // cardinality, if they immediately precede positional arguments.
1561
0
        else if (EQUAL(papszArgv[i], "-burn") && papszArgv[i + 1])
1562
0
        {
1563
0
            if (strchr(papszArgv[i + 1], ' '))
1564
0
            {
1565
0
                const CPLStringList aosTokens(
1566
0
                    CSLTokenizeString(papszArgv[i + 1]));
1567
0
                for (const char *pszToken : aosTokens)
1568
0
                {
1569
0
                    psOptions->adfBurnValues.push_back(CPLAtof(pszToken));
1570
0
                }
1571
0
                i += 1;
1572
0
            }
1573
0
            else
1574
0
            {
1575
0
                while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1576
0
                {
1577
0
                    psOptions->adfBurnValues.push_back(
1578
0
                        CPLAtof(papszArgv[i + 1]));
1579
0
                    i += 1;
1580
0
                }
1581
0
            }
1582
1583
            // Dummy value to make argparse happy, as at least one of
1584
            // -burn, -a or -3d is required
1585
0
            aosArgv.AddString("-burn");
1586
0
            aosArgv.AddString("0");
1587
0
        }
1588
0
        else if (EQUAL(papszArgv[i], "-init") && papszArgv[i + 1])
1589
0
        {
1590
0
            if (strchr(papszArgv[i + 1], ' '))
1591
0
            {
1592
0
                const CPLStringList aosTokens(
1593
0
                    CSLTokenizeString(papszArgv[i + 1]));
1594
0
                for (const char *pszToken : aosTokens)
1595
0
                {
1596
0
                    psOptions->adfInitVals.push_back(CPLAtof(pszToken));
1597
0
                }
1598
0
                i += 1;
1599
0
            }
1600
0
            else
1601
0
            {
1602
0
                while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1603
0
                {
1604
0
                    psOptions->adfInitVals.push_back(CPLAtof(papszArgv[i + 1]));
1605
0
                    i += 1;
1606
0
                }
1607
0
            }
1608
0
            psOptions->bCreateOutput = true;
1609
0
        }
1610
0
        else if (EQUAL(papszArgv[i], "-b") && papszArgv[i + 1])
1611
0
        {
1612
0
            if (strchr(papszArgv[i + 1], ' '))
1613
0
            {
1614
0
                const CPLStringList aosTokens(
1615
0
                    CSLTokenizeString(papszArgv[i + 1]));
1616
0
                for (const char *pszToken : aosTokens)
1617
0
                {
1618
0
                    psOptions->anBandList.push_back(atoi(pszToken));
1619
0
                }
1620
0
                i += 1;
1621
0
            }
1622
0
            else
1623
0
            {
1624
0
                while (i < argc - 1 && ArgIsNumericRasterize(papszArgv[i + 1]))
1625
0
                {
1626
0
                    psOptions->anBandList.push_back(atoi(papszArgv[i + 1]));
1627
0
                    i += 1;
1628
0
                }
1629
0
            }
1630
0
        }
1631
0
        else
1632
0
        {
1633
0
            aosArgv.AddString(papszArgv[i]);
1634
0
        }
1635
0
    }
1636
1637
0
    try
1638
0
    {
1639
0
        auto argParser =
1640
0
            GDALRasterizeOptionsGetParser(psOptions.get(), psOptionsForBinary);
1641
0
        argParser->parse_args_without_binary_name(aosArgv.List());
1642
1643
        // Check all no store_into args
1644
0
        if (auto oTe = argParser->present<std::vector<double>>("-te"))
1645
0
        {
1646
0
            psOptions->sEnvelop.MinX = oTe.value()[0];
1647
0
            psOptions->sEnvelop.MinY = oTe.value()[1];
1648
0
            psOptions->sEnvelop.MaxX = oTe.value()[2];
1649
0
            psOptions->sEnvelop.MaxY = oTe.value()[3];
1650
0
            psOptions->bCreateOutput = true;
1651
0
        }
1652
1653
0
        if (auto oTr = argParser->present<std::vector<double>>("-tr"))
1654
0
        {
1655
0
            psOptions->dfXRes = oTr.value()[0];
1656
0
            psOptions->dfYRes = oTr.value()[1];
1657
1658
0
            if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
1659
0
            {
1660
0
                CPLError(CE_Failure, CPLE_AppDefined,
1661
0
                         "Wrong value for -tr parameter.");
1662
0
                return nullptr;
1663
0
            }
1664
1665
0
            psOptions->bCreateOutput = true;
1666
0
        }
1667
1668
0
        if (auto oTs = argParser->present<std::vector<double>>("-ts"))
1669
0
        {
1670
0
            const int nXSize = static_cast<int>(oTs.value()[0]);
1671
0
            const int nYSize = static_cast<int>(oTs.value()[1]);
1672
1673
            // Warn the user if the conversion to int looses precision
1674
0
            if (nXSize != oTs.value()[0] || nYSize != oTs.value()[1])
1675
0
            {
1676
0
                CPLError(CE_Warning, CPLE_AppDefined,
1677
0
                         "-ts values parsed as %d %d.", nXSize, nYSize);
1678
0
            }
1679
1680
0
            psOptions->nXSize = nXSize;
1681
0
            psOptions->nYSize = nYSize;
1682
1683
0
            if (!(psOptions->nXSize > 0 || psOptions->nYSize > 0))
1684
0
            {
1685
0
                CPLError(CE_Failure, CPLE_AppDefined,
1686
0
                         "Wrong value for -ts parameter: at least one of the "
1687
0
                         "arguments must be greater than zero.");
1688
0
                return nullptr;
1689
0
            }
1690
1691
0
            psOptions->bCreateOutput = true;
1692
0
        }
1693
1694
0
        if (psOptions->bCreateOutput)
1695
0
        {
1696
0
            if (psOptions->dfXRes == 0 && psOptions->dfYRes == 0 &&
1697
0
                psOptions->nXSize == 0 && psOptions->nYSize == 0)
1698
0
            {
1699
0
                CPLError(CE_Failure, CPLE_NotSupported,
1700
0
                         "'-tr xres yres' or '-ts xsize ysize' is required.");
1701
0
                return nullptr;
1702
0
            }
1703
1704
0
            if (psOptions->bTargetAlignedPixels && psOptions->dfXRes == 0 &&
1705
0
                psOptions->dfYRes == 0)
1706
0
            {
1707
0
                CPLError(CE_Failure, CPLE_NotSupported,
1708
0
                         "-tap option cannot be used without using -tr.");
1709
0
                return nullptr;
1710
0
            }
1711
1712
0
            if (!psOptions->anBandList.empty())
1713
0
            {
1714
0
                CPLError(
1715
0
                    CE_Failure, CPLE_NotSupported,
1716
0
                    "-b option cannot be used when creating a GDAL dataset.");
1717
0
                return nullptr;
1718
0
            }
1719
1720
0
            int nBandCount = 1;
1721
1722
0
            if (!psOptions->adfBurnValues.empty())
1723
0
                nBandCount = static_cast<int>(psOptions->adfBurnValues.size());
1724
1725
0
            if (static_cast<int>(psOptions->adfInitVals.size()) > nBandCount)
1726
0
                nBandCount = static_cast<int>(psOptions->adfInitVals.size());
1727
1728
0
            if (psOptions->adfInitVals.size() == 1)
1729
0
            {
1730
0
                for (int i = 1; i <= nBandCount - 1; i++)
1731
0
                    psOptions->adfInitVals.push_back(psOptions->adfInitVals[0]);
1732
0
            }
1733
1734
0
            for (int i = 1; i <= nBandCount; i++)
1735
0
                psOptions->anBandList.push_back(i);
1736
0
        }
1737
0
        else
1738
0
        {
1739
0
            if (psOptions->anBandList.empty())
1740
0
                psOptions->anBandList.push_back(1);
1741
0
        }
1742
1743
0
        if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
1744
0
            !psOptions->osSQL.empty())
1745
0
        {
1746
0
            CPLError(CE_Warning, CPLE_AppDefined,
1747
0
                     "-dialect is ignored with -where. Use -sql instead");
1748
0
        }
1749
1750
0
        if (psOptionsForBinary)
1751
0
        {
1752
0
            psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
1753
0
            if (!psOptions->osFormat.empty())
1754
0
                psOptionsForBinary->osFormat = psOptions->osFormat;
1755
0
        }
1756
0
        else if (psOptions->adfBurnValues.empty() &&
1757
0
                 psOptions->osBurnAttribute.empty() && !psOptions->b3D)
1758
0
        {
1759
0
            psOptions->adfBurnValues.push_back(255);
1760
0
        }
1761
0
    }
1762
0
    catch (const std::exception &e)
1763
0
    {
1764
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1765
0
        return nullptr;
1766
0
    }
1767
1768
0
    return psOptions.release();
1769
0
}
1770
1771
/************************************************************************/
1772
/*                      GDALRasterizeOptionsFree()                      */
1773
/************************************************************************/
1774
1775
/**
1776
 * Frees the GDALRasterizeOptions struct.
1777
 *
1778
 * @param psOptions the options struct for GDALRasterize().
1779
 *
1780
 * @since GDAL 2.1
1781
 */
1782
1783
void GDALRasterizeOptionsFree(GDALRasterizeOptions *psOptions)
1784
0
{
1785
0
    delete psOptions;
1786
0
}
1787
1788
/************************************************************************/
1789
/*                  GDALRasterizeOptionsSetProgress()                   */
1790
/************************************************************************/
1791
1792
/**
1793
 * Set a progress function.
1794
 *
1795
 * @param psOptions the options struct for GDALRasterize().
1796
 * @param pfnProgress the progress callback.
1797
 * @param pProgressData the user data for the progress callback.
1798
 *
1799
 * @since GDAL 2.1
1800
 */
1801
1802
void GDALRasterizeOptionsSetProgress(GDALRasterizeOptions *psOptions,
1803
                                     GDALProgressFunc pfnProgress,
1804
                                     void *pProgressData)
1805
0
{
1806
0
    psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1807
0
    psOptions->pProgressData = pProgressData;
1808
0
}