Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_create.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "raster create" subcommand
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_raster_create.h"
14
15
#include "cpl_conv.h"
16
#include "gdal_priv.h"
17
#include "gdal_utils.h"
18
#include "ogr_spatialref.h"
19
20
//! @cond Doxygen_Suppress
21
22
#ifndef _
23
0
#define _(x) (x)
24
#endif
25
26
/************************************************************************/
27
/*        GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm()        */
28
/************************************************************************/
29
30
GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm(
31
    bool standaloneStep) noexcept
32
0
    : GDALRasterPipelineStepAlgorithm(
33
0
          NAME, DESCRIPTION, HELP_URL,
34
0
          ConstructorOptions()
35
0
              .SetStandaloneStep(standaloneStep)
36
0
              .SetAddDefaultArguments(false)
37
0
              .SetAutoOpenInputDatasets(true)
38
0
              .SetInputDatasetHelpMsg("Template raster dataset")
39
0
              .SetInputDatasetAlias("like")
40
0
              .SetInputDatasetMetaVar("TEMPLATE-DATASET")
41
0
              .SetInputDatasetRequired(false)
42
0
              .SetInputDatasetPositional(false)
43
0
              .SetInputDatasetMaxCount(1))
44
0
{
45
0
    AddRasterInputArgs(false, false);
46
0
    if (standaloneStep)
47
0
    {
48
0
        AddProgressArg();
49
0
        AddRasterOutputArgs(false);
50
0
    }
51
52
0
    auto checkResSizeInput = [this](const std::vector<std::string> &values,
53
0
                                    const std::string &arg_name,
54
0
                                    bool requiresInt)
55
0
    {
56
0
        for (const auto &s : values)
57
0
        {
58
0
            char *endptr = nullptr;
59
0
            const double val = CPLStrtod(s.c_str(), &endptr);
60
0
            bool ok = false;
61
0
            if (endptr == s.c_str() + s.size())
62
0
            {
63
0
                if (val >= 0 && val <= INT_MAX &&
64
0
                    (!requiresInt || static_cast<int>(val) == val))
65
0
                {
66
0
                    ok = true;
67
0
                }
68
0
            }
69
0
            else if (endptr &&
70
0
                     ((endptr[0] == ' ' && endptr[1] == '%' &&
71
0
                       endptr + 2 == s.c_str() + s.size()) ||
72
0
                      (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
73
0
            {
74
0
                if (val >= 0)
75
0
                {
76
0
                    ok = true;
77
0
                }
78
0
            }
79
0
            if (!ok)
80
0
            {
81
0
                ReportError(CE_Failure, CPLE_IllegalArg,
82
0
                            "Invalid %s value: %s'", arg_name.c_str(),
83
0
                            s.c_str());
84
0
                return false;
85
0
            }
86
0
        }
87
0
        return true;
88
0
    };
89
90
0
    AddArg("resolution", 0, _("Target resolution (in destination CRS units)"),
91
0
           &m_resolution_str)
92
0
        .SetMinCount(2)
93
0
        .SetMaxCount(2)
94
0
        .SetMinValueExcluded(0)
95
0
        .SetRepeatedArgAllowed(false)
96
0
        .SetDisplayHintAboutRepetition(false)
97
0
        .SetMetaVar("<xres[%]>,<yres[%]>")
98
0
        .SetMutualExclusionGroup("resolution-size")
99
0
        .AddValidationAction(
100
0
            [this, checkResSizeInput]()
101
0
            {
102
0
                return checkResSizeInput(m_resolution_str, "resolution", false);
103
0
            });
104
105
    // The same logic was applied in gdalalg_raster_resize.cpp, so we replicate it here for consistency.
106
0
    AddArg("size", 0,
107
0
           _("Target size in pixels (or percentage if using '%' suffix)"),
108
0
           &m_size_str)
109
0
        .SetMinCount(2)
110
0
        .SetMaxCount(2)
111
0
        .SetMinValueIncluded(0)
112
0
        .SetRepeatedArgAllowed(false)
113
0
        .SetDisplayHintAboutRepetition(false)
114
0
        .SetMetaVar("<width[%]>,<height[%]>")
115
0
        .SetMutualExclusionGroup("resolution-size")
116
0
        .AddValidationAction(
117
0
            [this, checkResSizeInput]()
118
0
            { return checkResSizeInput(m_size_str, "size", true); });
119
120
0
    AddArg("band-count", 0, _("Number of bands"), &m_bandCount)
121
0
        .SetDefault(m_bandCount)
122
0
        .SetMinValueIncluded(0);
123
0
    AddOutputDataTypeArg(&m_type).SetDefault(m_type);
124
125
0
    AddNodataArg(&m_nodata, /* noneAllowed = */ true);
126
127
0
    AddArg("burn", 0, _("Burn value"), &m_burnValues);
128
0
    AddArg("crs", 0, _("Set CRS"), &m_crs)
129
0
        .AddHiddenAlias("a_srs")
130
0
        .SetIsCRSArg(/*noneAllowed=*/true);
131
0
    AddBBOXArg(&m_bbox);
132
133
0
    {
134
0
        auto &arg = AddArg("metadata", 0, _("Add metadata item"), &m_metadata)
135
0
                        .SetMetaVar("<KEY>=<VALUE>")
136
0
                        .SetPackedValuesAllowed(false);
137
0
        arg.AddValidationAction([this, &arg]()
138
0
                                { return ParseAndValidateKeyValue(arg); });
139
0
        arg.AddHiddenAlias("mo");
140
0
    }
141
142
0
    const auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
143
0
    CPLAssertNotNull(inputArg);
144
145
0
    AddArg("copy-metadata", 0, _("Copy metadata from input dataset"),
146
0
           &m_copyMetadata)
147
0
        .AddDirectDependency(*inputArg);
148
0
    AddArg("copy-overviews", 0,
149
0
           _("Create same overview levels as input dataset"), &m_copyOverviews)
150
0
        .AddDirectDependency(*inputArg);
151
0
}
152
153
/************************************************************************/
154
/*                 GDALRasterCreateAlgorithm::RunImpl()                 */
155
/************************************************************************/
156
157
bool GDALRasterCreateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
158
                                        void *pProgressData)
159
0
{
160
0
    GDALPipelineStepRunContext stepCtxt;
161
0
    stepCtxt.m_pfnProgress = pfnProgress;
162
0
    stepCtxt.m_pProgressData = pProgressData;
163
0
    return RunPreStepPipelineValidations() && RunStep(stepCtxt);
164
0
}
165
166
/************************************************************************/
167
/*                 GDALRasterCreateAlgorithm::RunStep()                 */
168
/************************************************************************/
169
170
bool GDALRasterCreateAlgorithm::RunStep(GDALPipelineStepRunContext &)
171
0
{
172
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
173
174
0
    if (m_standaloneStep)
175
0
    {
176
0
        if (m_format.empty())
177
0
        {
178
0
            const auto aosFormats =
179
0
                CPLStringList(GDALGetOutputDriversForDatasetName(
180
0
                    m_outputDataset.GetName().c_str(), GDAL_OF_RASTER,
181
0
                    /* bSingleMatch = */ true,
182
0
                    /* bWarn = */ true));
183
0
            if (aosFormats.size() != 1)
184
0
            {
185
0
                ReportError(CE_Failure, CPLE_AppDefined,
186
0
                            "Cannot guess driver for %s",
187
0
                            m_outputDataset.GetName().c_str());
188
0
                return false;
189
0
            }
190
0
            m_format = aosFormats[0];
191
0
        }
192
0
    }
193
0
    else
194
0
    {
195
0
        m_format = "MEM";
196
0
    }
197
198
0
    OGRSpatialReference oSRS;
199
200
0
    GDALGeoTransform gt;
201
0
    bool bGTValid = false;
202
203
0
    CPLStringList aosCreationOptions(m_creationOptions);
204
205
0
    GDALDataset *poSrcDS = m_inputDataset.empty()
206
0
                               ? nullptr
207
0
                               : m_inputDataset.front().GetDatasetRef();
208
209
0
    constexpr double EPSILON = 1e-5;
210
211
    // Process size_str to fill m_size
212
0
    for (size_t i = 0; i < m_size_str.size(); i++)
213
0
    {
214
0
        const auto &s = m_size_str[i];
215
0
        char *endptr = nullptr;
216
0
        const double val = CPLStrtod(s.c_str(), &endptr);
217
0
        if (endptr &&
218
0
            ((endptr[0] == ' ' && endptr[1] == '%' &&
219
0
              endptr + 2 == s.c_str() + s.size()) ||
220
0
             (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
221
0
        {
222
            // Percentage
223
0
            if (!poSrcDS)
224
0
            {
225
0
                ReportError(CE_Failure, CPLE_AppDefined,
226
0
                            "Cannot use percentage size without input dataset");
227
0
                return false;
228
0
            }
229
0
            const int refSize = (i == 0) ? poSrcDS->GetRasterXSize()
230
0
                                         : poSrcDS->GetRasterYSize();
231
0
            const double dfSize = std::ceil((refSize * val / 100.0) - EPSILON);
232
0
            ;
233
0
            if (dfSize > INT_MAX)
234
0
            {
235
0
                ReportError(CE_Failure, CPLE_AppDefined,
236
0
                            "Computed size is too large");
237
0
                return false;
238
0
            }
239
0
            m_size.push_back(static_cast<int>(dfSize));
240
0
        }
241
0
        else
242
0
        {
243
0
            m_size.push_back(static_cast<int>(val));
244
0
        }
245
0
    }
246
247
0
    std::vector<bool> abResIsPercentage(m_resolution_str.size(), false);
248
249
    // Process resolution_str to fill m_resolution
250
0
    for (size_t i = 0; i < m_resolution_str.size(); i++)
251
0
    {
252
0
        const auto &s = m_resolution_str[i];
253
0
        char *endptr = nullptr;
254
0
        const double val = CPLStrtod(s.c_str(), &endptr);
255
256
0
        if (endptr &&
257
0
            ((endptr[0] == ' ' && endptr[1] == '%' &&
258
0
              endptr + 2 == s.c_str() + s.size()) ||
259
0
             (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
260
0
        {
261
            // Percentage
262
0
            if (!poSrcDS)
263
0
            {
264
0
                ReportError(
265
0
                    CE_Failure, CPLE_AppDefined,
266
0
                    "Cannot use percentage resolution without input dataset");
267
0
                return false;
268
0
            }
269
0
            if (poSrcDS->GetGeoTransform(gt) == CE_None)
270
0
            {
271
0
                const double refRes =
272
0
                    (i == 0) ? std::abs(gt.xscale) : std::abs(gt.yscale);
273
0
                const double dfRes = refRes * (val / 100.0);
274
0
                m_resolution.push_back(dfRes);
275
0
            }
276
0
            else
277
0
            {
278
0
                ReportError(CE_Failure, CPLE_AppDefined,
279
0
                            "Cannot get geotransform from input dataset");
280
0
                return false;
281
0
            }
282
0
            abResIsPercentage[i] = true;
283
0
        }
284
0
        else
285
0
        {
286
0
            m_resolution.push_back(val);
287
0
            abResIsPercentage[i] = false;
288
0
        }
289
0
    }
290
291
0
    if (poSrcDS)
292
0
    {
293
0
        if (m_size.empty())
294
0
        {
295
0
            m_size = std::vector<int>{poSrcDS->GetRasterXSize(),
296
0
                                      poSrcDS->GetRasterYSize()};
297
0
        }
298
299
0
        bGTValid = poSrcDS->GetGeoTransform(gt) == CE_None;
300
301
        // If one of the size is 0, compute it from the other size
302
0
        if (m_size[0] == 0 && m_size[1] > 0)
303
0
        {
304
0
            if (bGTValid)
305
0
            {
306
0
                const double ratio =
307
0
                    static_cast<double>(poSrcDS->GetRasterXSize()) /
308
0
                    static_cast<double>(poSrcDS->GetRasterYSize());
309
0
                const double dfWidth =
310
0
                    std::ceil(static_cast<double>(m_size[1]) * ratio - EPSILON);
311
0
                if (dfWidth > INT_MAX)
312
0
                {
313
0
                    ReportError(CE_Failure, CPLE_AppDefined,
314
0
                                "Computed width is too large");
315
0
                    return false;
316
0
                }
317
0
                m_size[0] = static_cast<int>(dfWidth);
318
0
            }
319
0
            else
320
0
            {
321
0
                ReportError(CE_Failure, CPLE_AppDefined,
322
0
                            "Cannot get geotransform from input dataset");
323
0
                return false;
324
0
            }
325
0
        }
326
0
        else if (m_size[1] == 0 && m_size[0] > 0)
327
0
        {
328
0
            if (bGTValid)
329
0
            {
330
0
                const double ratio =
331
0
                    static_cast<double>(poSrcDS->GetRasterYSize()) /
332
0
                    static_cast<double>(poSrcDS->GetRasterXSize());
333
0
                const double dfHeight =
334
0
                    std::ceil(static_cast<double>(m_size[0]) * ratio - EPSILON);
335
0
                if (dfHeight > INT_MAX)
336
0
                {
337
0
                    ReportError(CE_Failure, CPLE_AppDefined,
338
0
                                "Computed height is too large");
339
0
                    return false;
340
0
                }
341
0
                m_size[1] = static_cast<int>(dfHeight);
342
0
            }
343
0
            else
344
0
            {
345
0
                m_size[1] = poSrcDS->GetRasterYSize();
346
0
            }
347
0
        }
348
349
0
        if (!GetArg("band-count")->IsExplicitlySet())
350
0
        {
351
0
            m_bandCount = poSrcDS->GetRasterCount();
352
0
        }
353
354
0
        if (!GetArg("datatype")->IsExplicitlySet())
355
0
        {
356
0
            if (m_bandCount > 0)
357
0
            {
358
0
                m_type = GDALGetDataTypeName(
359
0
                    poSrcDS->GetRasterBand(1)->GetRasterDataType());
360
0
            }
361
0
        }
362
363
0
        if (m_crs.empty())
364
0
        {
365
0
            if (const auto poSRS = poSrcDS->GetSpatialRef())
366
0
                oSRS = *poSRS;
367
0
        }
368
369
0
        if (m_nodata.empty() && m_bandCount > 0)
370
0
        {
371
0
            int bNoData = false;
372
0
            const double dfNoData =
373
0
                poSrcDS->GetRasterBand(1)->GetNoDataValue(&bNoData);
374
0
            if (bNoData)
375
0
                m_nodata = CPLSPrintf("%.17g", dfNoData);
376
0
        }
377
378
        // Replicate tiling of input datasets for a few popular output formats,
379
        // when compatible, and when the user hasn't specified creation options
380
        // affecting tiling.
381
0
        int nBlockXSize = 0, nBlockYSize = 0;
382
0
        if (m_bandCount > 0)
383
0
            poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
384
385
0
        if (EQUAL(m_format.c_str(), "GTIFF") &&
386
0
            aosCreationOptions.FetchNameValue("TILED") == nullptr &&
387
0
            aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
388
0
            aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
389
0
            m_bandCount > 0)
390
0
        {
391
0
            if (nBlockXSize != poSrcDS->GetRasterXSize() &&
392
0
                (nBlockXSize % 16) == 0 && (nBlockYSize % 16) == 0)
393
0
            {
394
0
                aosCreationOptions.SetNameValue("TILED", "YES");
395
0
                aosCreationOptions.SetNameValue("BLOCKXSIZE",
396
0
                                                CPLSPrintf("%d", nBlockXSize));
397
0
                aosCreationOptions.SetNameValue("BLOCKYSIZE",
398
0
                                                CPLSPrintf("%d", nBlockYSize));
399
0
            }
400
0
        }
401
0
        else if (EQUAL(m_format.c_str(), "COG") &&
402
0
                 aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
403
0
                 m_bandCount > 0)
404
0
        {
405
0
            if (nBlockXSize != poSrcDS->GetRasterXSize() &&
406
0
                nBlockXSize == nBlockYSize && nBlockXSize >= 128 &&
407
0
                (nBlockXSize % 16) == 0)
408
0
            {
409
0
                aosCreationOptions.SetNameValue("BLOCKSIZE",
410
0
                                                CPLSPrintf("%d", nBlockXSize));
411
0
            }
412
0
        }
413
0
        else if (EQUAL(m_format.c_str(), "GPKG") &&
414
0
                 aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
415
0
                 aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
416
0
                 aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
417
0
                 m_bandCount > 0)
418
0
        {
419
0
            if (nBlockXSize != poSrcDS->GetRasterXSize() &&
420
0
                nBlockXSize >= 256 && nBlockXSize <= 4096 &&
421
0
                nBlockYSize >= 256 && nBlockYSize <= 4096)
422
0
            {
423
0
                aosCreationOptions.SetNameValue("BLOCKXSIZE",
424
0
                                                CPLSPrintf("%d", nBlockXSize));
425
0
                aosCreationOptions.SetNameValue("BLOCKYSIZE",
426
0
                                                CPLSPrintf("%d", nBlockYSize));
427
0
            }
428
0
        }
429
430
        // If resolution was explicitly set, then we need to recompute size
431
0
        if (!m_resolution.empty() && bGTValid)
432
0
        {
433
            // Set resolution from the other axis if 0
434
0
            if (m_resolution[0] == 0)
435
0
            {
436
0
                if (abResIsPercentage[1])
437
0
                {
438
0
                    m_resolution[0] = (std::abs(gt.xscale) / m_resolution[1]) /
439
0
                                      std::abs(gt.yscale);
440
0
                }
441
0
                else
442
0
                {
443
0
                    m_resolution[0] = m_resolution[1];
444
0
                }
445
0
            }
446
447
0
            if (m_resolution[1] == 0)
448
0
            {
449
0
                if (abResIsPercentage[0])
450
0
                {
451
0
                    m_resolution[1] = (std::abs(gt.yscale) / m_resolution[0]) /
452
0
                                      std::abs(gt.xscale);
453
0
                }
454
0
                else
455
0
                {
456
0
                    m_resolution[1] = m_resolution[0];
457
0
                }
458
0
            }
459
460
0
            const double dfXResRatio = std::abs(gt.xscale) / m_resolution[0];
461
0
            const double dfYResRatio = std::abs(gt.yscale) / m_resolution[1];
462
0
            const double dfWidth =
463
0
                std::ceil(poSrcDS->GetRasterXSize() * dfXResRatio - EPSILON);
464
0
            const double dfHeight =
465
0
                std::ceil(poSrcDS->GetRasterYSize() * dfYResRatio - EPSILON);
466
0
            if (dfWidth > INT_MAX || dfHeight > INT_MAX)
467
0
            {
468
0
                ReportError(CE_Failure, CPLE_AppDefined,
469
0
                            "Computed size is too large");
470
0
                return false;
471
0
            }
472
0
            m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
473
0
        }
474
0
    }
475
476
    // If size is empty, try resolution from bbox
477
0
    if (m_size.empty() && m_bbox.size() == 4 && m_resolution.size() == 2 &&
478
0
        (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
479
0
    {
480
0
        const double dfWidth =
481
0
            std::ceil((m_bbox[2] - m_bbox[0]) / m_resolution[0] - EPSILON);
482
0
        const double dfHeight =
483
0
            std::ceil((m_bbox[3] - m_bbox[1]) / m_resolution[1] - EPSILON);
484
0
        if (dfWidth > INT_MAX || dfHeight > INT_MAX)
485
0
        {
486
0
            ReportError(CE_Failure, CPLE_AppDefined,
487
0
                        "Computed size is too large");
488
0
            return false;
489
0
        }
490
0
        m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
491
0
    }
492
493
0
    if (m_size.empty())
494
0
    {
495
0
        if (!m_resolution.empty() && m_bbox.empty())
496
0
        {
497
0
            ReportError(
498
0
                CE_Failure, CPLE_IllegalArg,
499
0
                "Cannot use resolution without 'bbox' or 'like' dataset");
500
0
        }
501
0
        else
502
0
        {
503
0
            ReportError(CE_Failure, CPLE_IllegalArg,
504
0
                        "Argument 'size' or 'resolution' or 'like' dataset "
505
0
                        "should be specified");
506
0
        }
507
0
        return false;
508
0
    }
509
510
    // Guess the size from bbox if only one of the two is specified
511
0
    if (m_size.size() == 2 && (m_size[0] == 0 || m_size[1] == 0) &&
512
0
        !(m_size[0] == 0 && m_size[1] == 0) && m_bbox.size() == 4 &&
513
0
        (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
514
0
    {
515
0
        const double ratio = (m_bbox[2] - m_bbox[0]) / (m_bbox[3] - m_bbox[1]);
516
0
        if (m_size[0] == 0)
517
0
        {
518
0
            double dfWidth = std::ceil(m_size[1] * ratio - EPSILON);
519
0
            if (dfWidth > INT_MAX)
520
0
            {
521
0
                ReportError(CE_Failure, CPLE_AppDefined,
522
0
                            "Too large computed width");
523
0
                return false;
524
0
            }
525
0
            m_size[0] = static_cast<int>(dfWidth);
526
0
        }
527
0
        else if (m_size[1] == 0)
528
0
        {
529
0
            double dfHeight = std::ceil(m_size[0] / ratio - EPSILON);
530
0
            if (dfHeight > INT_MAX)
531
0
            {
532
0
                ReportError(CE_Failure, CPLE_AppDefined,
533
0
                            "Too large computed height");
534
0
                return false;
535
0
            }
536
0
            m_size[1] = static_cast<int>(dfHeight);
537
0
        }
538
0
    }
539
540
0
    if (!m_burnValues.empty() && m_burnValues.size() != 1 &&
541
0
        static_cast<int>(m_burnValues.size()) != m_bandCount)
542
0
    {
543
0
        if (m_bandCount == 1)
544
0
        {
545
0
            ReportError(CE_Failure, CPLE_IllegalArg,
546
0
                        "One value should be provided for argument "
547
0
                        "'burn', given there is one band");
548
0
        }
549
0
        else
550
0
        {
551
0
            ReportError(CE_Failure, CPLE_IllegalArg,
552
0
                        "One or %d values should be provided for argument "
553
0
                        "'burn', given there are %d bands",
554
0
                        m_bandCount, m_bandCount);
555
0
        }
556
0
        return false;
557
0
    }
558
559
0
    auto poDriver = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
560
0
    if (!poDriver)
561
0
    {
562
        // shouldn't happen given checks done in GDALAlgorithm
563
0
        ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
564
0
                    m_format.c_str());
565
0
        return false;
566
0
    }
567
568
0
    if (m_appendRaster)
569
0
    {
570
0
        if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS) == nullptr)
571
0
        {
572
0
            ReportError(CE_Failure, CPLE_NotSupported,
573
0
                        "-append option not supported for driver %s",
574
0
                        poDriver->GetDescription());
575
0
            return false;
576
0
        }
577
0
        aosCreationOptions.SetNameValue("APPEND_SUBDATASET", "YES");
578
0
    }
579
580
0
    auto poRetDS = std::unique_ptr<GDALDataset>(poDriver->Create(
581
0
        m_outputDataset.GetName().c_str(), m_size[0], m_size[1], m_bandCount,
582
0
        GDALGetDataTypeByName(m_type.c_str()), aosCreationOptions.List()));
583
0
    if (!poRetDS)
584
0
    {
585
0
        return false;
586
0
    }
587
588
0
    if (!m_crs.empty() && m_crs != "none" && m_crs != "null")
589
0
    {
590
0
        oSRS.SetFromUserInput(m_crs.c_str());
591
0
        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
592
0
    }
593
594
0
    if (!oSRS.IsEmpty())
595
0
    {
596
0
        if (poRetDS->SetSpatialRef(&oSRS) != CE_None)
597
0
        {
598
0
            ReportError(CE_Failure, CPLE_AppDefined, "Setting CRS failed");
599
0
            return false;
600
0
        }
601
0
    }
602
603
0
    if (!m_bbox.empty())
604
0
    {
605
0
        if (poRetDS->GetRasterXSize() == 0 || poRetDS->GetRasterYSize() == 0)
606
0
        {
607
0
            ReportError(CE_Failure, CPLE_AppDefined,
608
0
                        "Cannot set extent because one of dataset height or "
609
0
                        "width is null");
610
0
            return false;
611
0
        }
612
0
        bGTValid = true;
613
0
        gt.xorig = m_bbox[0];
614
0
        gt.xscale = (m_bbox[2] - m_bbox[0]) / poRetDS->GetRasterXSize();
615
0
        gt.xrot = 0;
616
0
        gt.yorig = m_bbox[3];
617
0
        gt.yrot = 0;
618
0
        gt.yscale = -(m_bbox[3] - m_bbox[1]) / poRetDS->GetRasterYSize();
619
0
    }
620
0
    if (bGTValid)
621
0
    {
622
0
        if (poRetDS->SetGeoTransform(gt) != CE_None)
623
0
        {
624
0
            ReportError(CE_Failure, CPLE_AppDefined, "Setting extent failed");
625
0
            return false;
626
0
        }
627
0
    }
628
629
0
    if (!m_nodata.empty() && !EQUAL(m_nodata.c_str(), "none"))
630
0
    {
631
0
        for (int i = 0; i < poRetDS->GetRasterCount(); ++i)
632
0
        {
633
0
            bool bCannotBeExactlyRepresented = false;
634
0
            if (poRetDS->GetRasterBand(i + 1)->SetNoDataValueAsString(
635
0
                    m_nodata.c_str(), &bCannotBeExactlyRepresented) != CE_None)
636
0
            {
637
0
                if (bCannotBeExactlyRepresented)
638
0
                {
639
0
                    ReportError(CE_Failure, CPLE_AppDefined,
640
0
                                "Setting nodata value failed as it cannot be "
641
0
                                "represented on its data type");
642
0
                }
643
0
                else
644
0
                {
645
0
                    ReportError(CE_Failure, CPLE_AppDefined,
646
0
                                "Setting nodata value failed");
647
0
                }
648
0
                return false;
649
0
            }
650
0
        }
651
0
    }
652
653
0
    if (m_copyMetadata)
654
0
    {
655
656
        // This should never happen because of the dependency set
657
0
        CPLAssertNotNull(poSrcDS);
658
659
0
        {
660
0
            const CPLStringList aosDomains(poSrcDS->GetMetadataDomainList());
661
0
            for (const char *domain : aosDomains)
662
0
            {
663
0
                if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
664
0
                {
665
0
                    if (poRetDS->SetMetadata(poSrcDS->GetMetadata(domain),
666
0
                                             domain) != CE_None)
667
0
                    {
668
0
                        ReportError(CE_Failure, CPLE_AppDefined,
669
0
                                    "Cannot copy '%s' metadata domain", domain);
670
0
                        return false;
671
0
                    }
672
0
                }
673
0
            }
674
0
        }
675
0
        for (int i = 0; i < m_bandCount; ++i)
676
0
        {
677
0
            const CPLStringList aosDomains(
678
0
                poSrcDS->GetRasterBand(i + 1)->GetMetadataDomainList());
679
0
            for (const char *domain : aosDomains)
680
0
            {
681
0
                if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
682
0
                {
683
0
                    if (poRetDS->GetRasterBand(i + 1)->SetMetadata(
684
0
                            poSrcDS->GetRasterBand(i + 1)->GetMetadata(domain),
685
0
                            domain) != CE_None)
686
0
                    {
687
0
                        ReportError(
688
0
                            CE_Failure, CPLE_AppDefined,
689
0
                            "Cannot copy '%s' metadata domain for band %d",
690
0
                            domain, i + 1);
691
0
                        return false;
692
0
                    }
693
0
                }
694
0
            }
695
0
        }
696
0
    }
697
698
0
    const CPLStringList aosMD(m_metadata);
699
0
    for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
700
0
    {
701
0
        if (poRetDS->SetMetadataItem(key, value) != CE_None)
702
0
        {
703
0
            ReportError(CE_Failure, CPLE_AppDefined,
704
0
                        "SetMetadataItem('%s', '%s') failed", key, value);
705
0
            return false;
706
0
        }
707
0
    }
708
709
0
    if (m_copyOverviews && m_bandCount > 0)
710
0
    {
711
        // This should never happen because of the dependency set
712
0
        CPLAssertNotNull(poSrcDS);
713
714
0
        if (poSrcDS->GetRasterXSize() != poRetDS->GetRasterXSize() ||
715
0
            poSrcDS->GetRasterYSize() != poRetDS->GetRasterYSize())
716
0
        {
717
0
            ReportError(CE_Failure, CPLE_AppDefined,
718
0
                        "Argument 'copy-overviews' can only be set when the "
719
0
                        "input and output datasets have the same dimension");
720
0
            return false;
721
0
        }
722
0
        const int nOverviewCount =
723
0
            poSrcDS->GetRasterBand(1)->GetOverviewCount();
724
0
        std::vector<int> anLevels;
725
0
        for (int i = 0; i < nOverviewCount; ++i)
726
0
        {
727
0
            const auto poOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(i);
728
0
            const int nOvrFactor = GDALComputeOvFactor(
729
0
                poOvrBand->GetXSize(), poSrcDS->GetRasterXSize(),
730
0
                poOvrBand->GetYSize(), poSrcDS->GetRasterYSize());
731
0
            anLevels.push_back(nOvrFactor);
732
0
        }
733
0
        if (poRetDS->BuildOverviews(
734
0
                "NONE", nOverviewCount, anLevels.data(),
735
0
                /* nListBands = */ 0, /* panBandList = */ nullptr,
736
0
                /* pfnProgress = */ nullptr, /* pProgressData = */ nullptr,
737
0
                /* options = */ nullptr) != CE_None)
738
0
        {
739
0
            ReportError(CE_Failure, CPLE_AppDefined,
740
0
                        "Creating overview(s) failed");
741
0
            return false;
742
0
        }
743
0
    }
744
745
0
    if (!m_burnValues.empty())
746
0
    {
747
0
        for (int i = 0; i < m_bandCount; ++i)
748
0
        {
749
0
            const int burnValueIdx = m_burnValues.size() == 1 ? 0 : i;
750
0
            const auto poDstBand = poRetDS->GetRasterBand(i + 1);
751
            // cppcheck-suppress negativeContainerIndex
752
0
            if (poDstBand->Fill(m_burnValues[burnValueIdx]) != CE_None)
753
0
            {
754
0
                ReportError(CE_Failure, CPLE_AppDefined,
755
0
                            "Setting burn value failed");
756
0
                return false;
757
0
            }
758
0
        }
759
0
        if (poRetDS->FlushCache(false) != CE_None)
760
0
        {
761
0
            ReportError(CE_Failure, CPLE_AppDefined,
762
0
                        "Setting burn value failed");
763
0
            return false;
764
0
        }
765
0
    }
766
767
0
    m_outputDataset.Set(std::move(poRetDS));
768
769
0
    return true;
770
0
}
771
772
0
GDALRasterCreateAlgorithmStandalone::~GDALRasterCreateAlgorithmStandalone() =
773
    default;
774
775
//! @endcond