Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_edit.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "edit" step of "raster pipeline"
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_raster_edit.h"
14
15
#include "gdal_priv.h"
16
#include "gdal_utils.h"
17
#include "ogrsf_frmts.h"
18
19
#include <optional>
20
21
//! @cond Doxygen_Suppress
22
23
#ifndef _
24
0
#define _(x) (x)
25
#endif
26
27
/************************************************************************/
28
/*                           GetGCPFilename()                           */
29
/************************************************************************/
30
31
static std::string GetGCPFilename(const std::vector<std::string> &gcps)
32
0
{
33
0
    if (gcps.size() == 1 && !gcps[0].empty() && gcps[0][0] == '@')
34
0
    {
35
0
        return gcps[0].substr(1);
36
0
    }
37
0
    return std::string();
38
0
}
39
40
/************************************************************************/
41
/*          GDALRasterEditAlgorithm::GDALRasterEditAlgorithm()          */
42
/************************************************************************/
43
44
GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
45
0
    : GDALRasterPipelineStepAlgorithm(
46
0
          NAME, DESCRIPTION, HELP_URL,
47
0
          ConstructorOptions().SetAddDefaultArguments(false))
48
0
{
49
0
    if (standaloneStep)
50
0
    {
51
0
        AddProgressArg();
52
53
0
        AddArg("dataset", 0,
54
0
               _("Dataset (to be updated in-place, unless --auxiliary)"),
55
0
               &m_dataset, GDAL_OF_RASTER | GDAL_OF_UPDATE)
56
0
            .SetPositional()
57
0
            .SetRequired();
58
0
        AddOpenOptionsArg(&m_openOptions);
59
0
        AddArg("auxiliary", 0,
60
0
               _("Ask for an auxiliary .aux.xml file to be edited"),
61
0
               &m_readOnly)
62
0
            .AddHiddenAlias("ro")
63
0
            .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY);
64
0
    }
65
0
    else
66
0
    {
67
0
        AddRasterHiddenInputDatasetArg();
68
0
    }
69
70
0
    AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
71
0
        .AddHiddenAlias("a_srs")
72
0
        .AddHiddenAlias("srs")
73
0
        .SetIsCRSArg(/*noneAllowed=*/true);
74
75
0
    AddBBOXArg(&m_bbox);
76
77
0
    AddNodataArg(&m_nodata, /* noneAllowed = */ true);
78
79
0
    AddArg("color-interpretation", 0, _("Set band color interpretation"),
80
0
           &m_colorInterpretation)
81
0
        .SetAutoCompleteFunction(
82
0
            [this](const std::string &s)
83
0
            {
84
0
                std::vector<std::string> ret;
85
0
                int nValues = 0;
86
0
                const auto paeVals = GDALGetColorInterpretationList(&nValues);
87
0
                if (s.find('=') == std::string::npos)
88
0
                {
89
0
                    ret.push_back("all=");
90
0
                    if (auto poDS = m_dataset.GetDatasetRef())
91
0
                    {
92
0
                        for (int i = 0; i < poDS->GetRasterCount(); ++i)
93
0
                            ret.push_back(std::to_string(i + 1).append("="));
94
0
                    }
95
0
                    for (int i = 0; i < nValues; ++i)
96
0
                        ret.push_back(
97
0
                            GDALGetColorInterpretationName(paeVals[i]));
98
0
                }
99
0
                else
100
0
                {
101
0
                    for (int i = 0; i < nValues; ++i)
102
0
                        ret.push_back(
103
0
                            GDALGetColorInterpretationName(paeVals[i]));
104
0
                }
105
0
                return ret;
106
0
            });
107
108
0
    {
109
0
        auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
110
0
                           &m_metadata)
111
0
                        .SetMetaVar("<KEY>=<VALUE>")
112
0
                        .SetPackedValuesAllowed(false);
113
0
        arg.AddValidationAction([this, &arg]()
114
0
                                { return ParseAndValidateKeyValue(arg); });
115
0
        arg.AddHiddenAlias("mo");
116
0
    }
117
118
0
    AddArg("unset-metadata", 0, _("Remove dataset metadata item(s)"),
119
0
           &m_unsetMetadata)
120
0
        .SetMetaVar("<KEY>");
121
122
0
    AddArg("unset-metadata-domain", 0, _("Remove dataset metadata domain(s)"),
123
0
           &m_unsetMetadataDomain)
124
0
        .SetMetaVar("<DOMAIN>");
125
126
0
    AddArg("gcp", 0,
127
0
           _("Add ground control point, formatted as "
128
0
             "pixel,line,easting,northing[,elevation], or @filename"),
129
0
           &m_gcps)
130
0
        .SetPackedValuesAllowed(false)
131
0
        .AddValidationAction(
132
0
            [this]()
133
0
            {
134
0
                if (GetGCPFilename(m_gcps).empty())
135
0
                {
136
0
                    for (const std::string &gcp : m_gcps)
137
0
                    {
138
0
                        const CPLStringList aosTokens(
139
0
                            CSLTokenizeString2(gcp.c_str(), ",", 0));
140
0
                        if (aosTokens.size() != 4 && aosTokens.size() != 5)
141
0
                        {
142
0
                            ReportError(CE_Failure, CPLE_IllegalArg,
143
0
                                        "Bad format for %s", gcp.c_str());
144
0
                            return false;
145
0
                        }
146
0
                        for (int i = 0; i < aosTokens.size(); ++i)
147
0
                        {
148
0
                            if (CPLGetValueType(aosTokens[i]) ==
149
0
                                CPL_VALUE_STRING)
150
0
                            {
151
0
                                ReportError(CE_Failure, CPLE_IllegalArg,
152
0
                                            "Bad format for %s", gcp.c_str());
153
0
                                return false;
154
0
                            }
155
0
                        }
156
0
                    }
157
0
                }
158
0
                return true;
159
0
            });
160
161
0
    if (standaloneStep)
162
0
    {
163
0
        AddArg("stats", 0, _("Compute statistics, using all pixels"), &m_stats)
164
0
            .SetMutualExclusionGroup("stats");
165
0
        AddArg("approx-stats", 0,
166
0
               _("Compute statistics, using a subset of pixels"),
167
0
               &m_approxStats)
168
0
            .SetMutualExclusionGroup("stats");
169
0
        AddArg("hist", 0, _("Compute histogram"), &m_hist);
170
0
    }
171
0
}
172
173
/************************************************************************/
174
/*         GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm()          */
175
/************************************************************************/
176
177
0
GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm() = default;
178
179
/************************************************************************/
180
/*                             ParseGCPs()                              */
181
/************************************************************************/
182
183
std::vector<gdal::GCP> GDALRasterEditAlgorithm::ParseGCPs() const
184
0
{
185
0
    std::vector<gdal::GCP> ret;
186
0
    const std::string osGCPFilename = GetGCPFilename(m_gcps);
187
0
    if (!osGCPFilename.empty())
188
0
    {
189
0
        auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
190
0
            osGCPFilename.c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR));
191
0
        if (!poDS)
192
0
            return ret;
193
0
        if (poDS->GetLayerCount() != 1)
194
0
        {
195
0
            ReportError(CE_Failure, CPLE_AppDefined,
196
0
                        "GCPs can only be specified for single-layer datasets");
197
0
            return ret;
198
0
        }
199
0
        auto poLayer = poDS->GetLayer(0);
200
0
        const auto poLayerDefn = poLayer->GetLayerDefn();
201
0
        int nIdIdx = -1, nInfoIdx = -1, nColIdx = -1, nLineIdx = -1, nXIdx = -1,
202
0
            nYIdx = -1, nZIdx = -1;
203
204
0
        const struct
205
0
        {
206
0
            int &idx;
207
0
            const char *name;
208
0
            bool required;
209
0
        } aFields[] = {
210
0
            {nIdIdx, "id", false},     {nInfoIdx, "info", false},
211
0
            {nColIdx, "column", true}, {nLineIdx, "line", true},
212
0
            {nXIdx, "x", true},        {nYIdx, "y", true},
213
0
            {nZIdx, "z", false},
214
0
        };
215
216
0
        for (auto &field : aFields)
217
0
        {
218
0
            field.idx = poLayerDefn->GetFieldIndex(field.name);
219
0
            if (field.idx < 0 && field.required)
220
0
            {
221
0
                ReportError(CE_Failure, CPLE_AppDefined,
222
0
                            "Field '%s' cannot be found in '%s'", field.name,
223
0
                            poDS->GetDescription());
224
0
                return ret;
225
0
            }
226
0
        }
227
0
        for (auto &&poFeature : poLayer)
228
0
        {
229
0
            gdal::GCP gcp;
230
0
            if (nIdIdx >= 0)
231
0
                gcp.SetId(poFeature->GetFieldAsString(nIdIdx));
232
0
            if (nInfoIdx >= 0)
233
0
                gcp.SetInfo(poFeature->GetFieldAsString(nInfoIdx));
234
0
            gcp.Pixel() = poFeature->GetFieldAsDouble(nColIdx);
235
0
            gcp.Line() = poFeature->GetFieldAsDouble(nLineIdx);
236
0
            gcp.X() = poFeature->GetFieldAsDouble(nXIdx);
237
0
            gcp.Y() = poFeature->GetFieldAsDouble(nYIdx);
238
0
            if (nZIdx >= 0 && poFeature->IsFieldSetAndNotNull(nZIdx))
239
0
                gcp.Z() = poFeature->GetFieldAsDouble(nZIdx);
240
0
            ret.push_back(std::move(gcp));
241
0
        }
242
0
    }
243
0
    else
244
0
    {
245
0
        for (const std::string &gcpStr : m_gcps)
246
0
        {
247
0
            const CPLStringList aosTokens(
248
0
                CSLTokenizeString2(gcpStr.c_str(), ",", 0));
249
            // Verified by validation action
250
0
            CPLAssert(aosTokens.size() == 4 || aosTokens.size() == 5);
251
0
            gdal::GCP gcp;
252
0
            gcp.Pixel() = CPLAtof(aosTokens[0]);
253
0
            gcp.Line() = CPLAtof(aosTokens[1]);
254
0
            gcp.X() = CPLAtof(aosTokens[2]);
255
0
            gcp.Y() = CPLAtof(aosTokens[3]);
256
0
            if (aosTokens.size() == 5)
257
0
                gcp.Z() = CPLAtof(aosTokens[4]);
258
0
            ret.push_back(std::move(gcp));
259
0
        }
260
0
    }
261
0
    return ret;
262
0
}
263
264
/************************************************************************/
265
/*                  GDALRasterEditAlgorithm::RunStep()                  */
266
/************************************************************************/
267
268
bool GDALRasterEditAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
269
0
{
270
0
    GDALDataset *poDS = m_dataset.GetDatasetRef();
271
0
    if (poDS)
272
0
    {
273
0
        if (poDS->GetAccess() != GA_Update && !m_readOnly)
274
0
        {
275
0
            ReportError(CE_Failure, CPLE_AppDefined,
276
0
                        "Dataset should be opened in update mode unless "
277
0
                        "--auxiliary is set");
278
0
            return false;
279
0
        }
280
0
    }
281
0
    else
282
0
    {
283
0
        const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
284
0
        CPLAssert(poSrcDS);
285
0
        CPLAssert(m_outputDataset.GetName().empty());
286
0
        CPLAssert(!m_outputDataset.GetDatasetRef());
287
288
0
        CPLStringList aosOptions;
289
0
        aosOptions.push_back("-of");
290
0
        aosOptions.push_back("VRT");
291
0
        GDALTranslateOptions *psOptions =
292
0
            GDALTranslateOptionsNew(aosOptions.List(), nullptr);
293
0
        GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
294
0
        auto poRetDS = GDALDataset::FromHandle(
295
0
            GDALTranslate("", hSrcDS, psOptions, nullptr));
296
0
        GDALTranslateOptionsFree(psOptions);
297
0
        m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
298
0
        poDS = m_outputDataset.GetDatasetRef();
299
0
    }
300
301
0
    bool ret = poDS != nullptr;
302
303
0
    if (poDS)
304
0
    {
305
0
        if (m_overrideCrs == "null" || m_overrideCrs == "none")
306
0
        {
307
0
            if (poDS->SetSpatialRef(nullptr) != CE_None)
308
0
            {
309
0
                ReportError(CE_Failure, CPLE_AppDefined,
310
0
                            "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
311
0
                return false;
312
0
            }
313
0
        }
314
0
        else if (!m_overrideCrs.empty() && m_gcps.empty())
315
0
        {
316
0
            OGRSpatialReference oSRS;
317
0
            oSRS.SetFromUserInput(m_overrideCrs.c_str());
318
0
            oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
319
0
            if (poDS->SetSpatialRef(&oSRS) != CE_None)
320
0
            {
321
0
                ReportError(CE_Failure, CPLE_AppDefined,
322
0
                            "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
323
0
                return false;
324
0
            }
325
0
        }
326
327
0
        if (!m_bbox.empty())
328
0
        {
329
0
            if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
330
0
            {
331
0
                ReportError(CE_Failure, CPLE_AppDefined,
332
0
                            "Cannot set extent because one of dataset height "
333
0
                            "or width is null");
334
0
                return false;
335
0
            }
336
0
            GDALGeoTransform gt;
337
0
            gt.xorig = m_bbox[0];
338
0
            gt.xscale = (m_bbox[2] - m_bbox[0]) / poDS->GetRasterXSize();
339
0
            gt.xrot = 0;
340
0
            gt.yorig = m_bbox[3];
341
0
            gt.yrot = 0;
342
0
            gt.yscale = -(m_bbox[3] - m_bbox[1]) / poDS->GetRasterYSize();
343
0
            if (poDS->SetGeoTransform(gt) != CE_None)
344
0
            {
345
0
                ReportError(CE_Failure, CPLE_AppDefined,
346
0
                            "Setting extent failed");
347
0
                return false;
348
0
            }
349
0
        }
350
351
0
        if (!m_nodata.empty())
352
0
        {
353
0
            for (int i = 0; i < poDS->GetRasterCount(); ++i)
354
0
            {
355
0
                if (EQUAL(m_nodata.c_str(), "none"))
356
0
                    poDS->GetRasterBand(i + 1)->DeleteNoDataValue();
357
0
                else
358
0
                    poDS->GetRasterBand(i + 1)->SetNoDataValue(
359
0
                        CPLAtof(m_nodata.c_str()));
360
0
            }
361
0
        }
362
363
0
        if (!m_colorInterpretation.empty())
364
0
        {
365
0
            const auto GetColorInterp =
366
0
                [this](const char *pszStr) -> std::optional<GDALColorInterp>
367
0
            {
368
0
                if (EQUAL(pszStr, "undefined"))
369
0
                    return GCI_Undefined;
370
0
                const GDALColorInterp eInterp =
371
0
                    GDALGetColorInterpretationByName(pszStr);
372
0
                if (eInterp != GCI_Undefined)
373
0
                    return eInterp;
374
0
                ReportError(CE_Failure, CPLE_NotSupported,
375
0
                            "Unsupported color interpretation: %s", pszStr);
376
0
                return {};
377
0
            };
378
379
0
            if (m_colorInterpretation.size() == 1 &&
380
0
                poDS->GetRasterCount() > 1 &&
381
0
                !cpl::starts_with(m_colorInterpretation[0], "all="))
382
0
            {
383
0
                ReportError(
384
0
                    CE_Failure, CPLE_NotSupported,
385
0
                    "With several bands, specify as many color interpretation "
386
0
                    "as bands, one or many values of the form "
387
0
                    "<band_number>=<color> or a single value all=<color>");
388
0
                return false;
389
0
            }
390
0
            else
391
0
            {
392
0
                int nBandIter = 0;
393
0
                bool bSyntaxAll = false;
394
0
                bool bSyntaxExplicitBand = false;
395
0
                bool bSyntaxImplicitBand = false;
396
0
                for (const std::string &token : m_colorInterpretation)
397
0
                {
398
0
                    const CPLStringList aosTokens(
399
0
                        CSLTokenizeString2(token.c_str(), "=", 0));
400
0
                    if (aosTokens.size() == 2 && EQUAL(aosTokens[0], "all"))
401
0
                    {
402
0
                        bSyntaxAll = true;
403
0
                        const auto eColorInterp = GetColorInterp(aosTokens[1]);
404
0
                        if (!eColorInterp)
405
0
                        {
406
0
                            return false;
407
0
                        }
408
0
                        else
409
0
                        {
410
0
                            for (int i = 0; i < poDS->GetRasterCount(); ++i)
411
0
                            {
412
0
                                if (poDS->GetRasterBand(i + 1)
413
0
                                        ->SetColorInterpretation(
414
0
                                            *eColorInterp) != CE_None)
415
0
                                    return false;
416
0
                            }
417
0
                        }
418
0
                    }
419
0
                    else if (aosTokens.size() == 2)
420
0
                    {
421
0
                        bSyntaxExplicitBand = true;
422
0
                        const int nBand = atoi(aosTokens[0]);
423
0
                        if (nBand <= 0 || nBand > poDS->GetRasterCount())
424
0
                        {
425
0
                            ReportError(CE_Failure, CPLE_NotSupported,
426
0
                                        "Invalid band number '%s' in '%s'",
427
0
                                        aosTokens[0], token.c_str());
428
0
                            return false;
429
0
                        }
430
0
                        const auto eColorInterp = GetColorInterp(aosTokens[1]);
431
0
                        if (!eColorInterp)
432
0
                        {
433
0
                            return false;
434
0
                        }
435
0
                        else if (poDS->GetRasterBand(nBand)
436
0
                                     ->SetColorInterpretation(*eColorInterp) !=
437
0
                                 CE_None)
438
0
                        {
439
0
                            return false;
440
0
                        }
441
0
                    }
442
0
                    else
443
0
                    {
444
0
                        bSyntaxImplicitBand = true;
445
0
                        ++nBandIter;
446
0
                        if (nBandIter > poDS->GetRasterCount())
447
0
                        {
448
0
                            ReportError(CE_Failure, CPLE_IllegalArg,
449
0
                                        "More color interpretation values "
450
0
                                        "specified than bands in the dataset");
451
0
                            return false;
452
0
                        }
453
0
                        const auto eColorInterp = GetColorInterp(token.c_str());
454
0
                        if (!eColorInterp)
455
0
                        {
456
0
                            return false;
457
0
                        }
458
0
                        else if (poDS->GetRasterBand(nBandIter)
459
0
                                     ->SetColorInterpretation(*eColorInterp) !=
460
0
                                 CE_None)
461
0
                        {
462
0
                            return false;
463
0
                        }
464
0
                    }
465
0
                }
466
0
                if ((bSyntaxAll ? 1 : 0) + (bSyntaxExplicitBand ? 1 : 0) +
467
0
                        (bSyntaxImplicitBand ? 1 : 0) !=
468
0
                    1)
469
0
                {
470
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
471
0
                                "Mix of different syntaxes to specify color "
472
0
                                "interpretation");
473
0
                    return false;
474
0
                }
475
0
                if (bSyntaxImplicitBand && nBandIter != poDS->GetRasterCount())
476
0
                {
477
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
478
0
                                "Less color interpretation values specified "
479
0
                                "than bands in the dataset");
480
0
                    return false;
481
0
                }
482
0
            }
483
0
        }
484
485
0
        const CPLStringList aosMD(m_metadata);
486
0
        for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
487
0
        {
488
0
            if (poDS->SetMetadataItem(key, value) != CE_None)
489
0
            {
490
0
                ReportError(CE_Failure, CPLE_AppDefined,
491
0
                            "SetMetadataItem('%s', '%s') failed", key, value);
492
0
                return false;
493
0
            }
494
0
        }
495
496
0
        for (const std::string &key : m_unsetMetadata)
497
0
        {
498
0
            if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
499
0
            {
500
0
                ReportError(CE_Failure, CPLE_AppDefined,
501
0
                            "SetMetadataItem('%s', NULL) failed", key.c_str());
502
0
                return false;
503
0
            }
504
0
        }
505
506
0
        for (const std::string &domain : m_unsetMetadataDomain)
507
0
        {
508
0
            if (poDS->SetMetadata(nullptr, domain.c_str()) != CE_None)
509
0
            {
510
0
                ReportError(CE_Failure, CPLE_AppDefined,
511
0
                            "SetMetadata(NULL, '%s') failed", domain.c_str());
512
0
                return false;
513
0
            }
514
0
        }
515
516
0
        if (!m_gcps.empty())
517
0
        {
518
0
            const auto gcps = ParseGCPs();
519
0
            if (gcps.empty())
520
0
                return false;  // error already emitted by ParseGCPs()
521
522
0
            OGRSpatialReference oSRS;
523
0
            if (!m_overrideCrs.empty())
524
0
            {
525
0
                oSRS.SetFromUserInput(m_overrideCrs.c_str());
526
0
                oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
527
0
            }
528
529
0
            if (poDS->SetGCPs(static_cast<int>(gcps.size()), gcps[0].c_ptr(),
530
0
                              oSRS.IsEmpty() ? nullptr : &oSRS) != CE_None)
531
0
            {
532
0
                ReportError(CE_Failure, CPLE_AppDefined, "Setting GCPs failed");
533
0
                return false;
534
0
            }
535
0
        }
536
537
0
        const int nBands = poDS->GetRasterCount();
538
0
        int nCurProgress = 0;
539
0
        const double dfTotalProgress =
540
0
            ((m_stats || m_approxStats) ? nBands : 0) + (m_hist ? nBands : 0);
541
0
        if (m_stats || m_approxStats)
542
0
        {
543
0
            for (int i = 0; (i < nBands) && ret; ++i)
544
0
            {
545
0
                void *pScaledProgress = GDALCreateScaledProgress(
546
0
                    nCurProgress / dfTotalProgress,
547
0
                    (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
548
0
                    ctxt.m_pProgressData);
549
0
                ++nCurProgress;
550
0
                double dfMin = 0.0;
551
0
                double dfMax = 0.0;
552
0
                double dfMean = 0.0;
553
0
                double dfStdDev = 0.0;
554
0
                ret = poDS->GetRasterBand(i + 1)->ComputeStatistics(
555
0
                          m_approxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
556
0
                          pScaledProgress ? GDALScaledProgress : nullptr,
557
0
                          pScaledProgress) == CE_None;
558
0
                GDALDestroyScaledProgress(pScaledProgress);
559
0
            }
560
0
        }
561
562
0
        if (m_hist)
563
0
        {
564
0
            for (int i = 0; (i < nBands) && ret; ++i)
565
0
            {
566
0
                void *pScaledProgress = GDALCreateScaledProgress(
567
0
                    nCurProgress / dfTotalProgress,
568
0
                    (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
569
0
                    ctxt.m_pProgressData);
570
0
                ++nCurProgress;
571
0
                double dfMin = 0.0;
572
0
                double dfMax = 0.0;
573
0
                int nBucketCount = 0;
574
0
                GUIntBig *panHistogram = nullptr;
575
0
                ret = poDS->GetRasterBand(i + 1)->GetDefaultHistogram(
576
0
                          &dfMin, &dfMax, &nBucketCount, &panHistogram, TRUE,
577
0
                          pScaledProgress ? GDALScaledProgress : nullptr,
578
0
                          pScaledProgress) == CE_None;
579
0
                if (ret)
580
0
                {
581
0
                    ret = poDS->GetRasterBand(i + 1)->SetDefaultHistogram(
582
0
                              dfMin, dfMax, nBucketCount, panHistogram) ==
583
0
                          CE_None;
584
0
                }
585
0
                CPLFree(panHistogram);
586
0
                GDALDestroyScaledProgress(pScaledProgress);
587
0
            }
588
0
        }
589
0
    }
590
591
0
    return poDS != nullptr;
592
0
}
593
594
GDALRasterEditAlgorithmStandalone::~GDALRasterEditAlgorithmStandalone() =
595
    default;
596
597
//! @endcond