Coverage Report

Created: 2026-07-25 06:50

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