• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.33
/frmts/vrt/vrtprocesseddataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTProcessedDataset.
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "cpl_minixml.h"
14
#include "cpl_string.h"
15
#include "gdal_utils.h"
16
#include "vrtdataset.h"
17

18
#include <algorithm>
19
#include <limits>
20
#include <map>
21
#include <vector>
22

23
/************************************************************************/
24
/*                        VRTProcessedDatasetFunc                       */
25
/************************************************************************/
26

27
//! Structure holding information for a VRTProcessedDataset function.
28
struct VRTProcessedDatasetFunc
29
{
30
    //! Processing function name
31
    std::string osFuncName{};
32

33
    //! User data to provide to pfnInit, pfnFree, pfnProcess callbacks.
34
    void *pUserData = nullptr;
35

36
    //! Whether XML metadata has been specified
37
    bool bMetadataSpecified = false;
38

39
    //! Map of (constant argument name, constant value)
40
    std::map<std::string, std::string> oMapConstantArguments{};
41

42
    //! Set of builtin argument names (e.g "offset", "scale", "nodata")
43
    std::set<std::string> oSetBuiltinArguments{};
44

45
    //! Arguments defined in the VRT
46
    struct OtherArgument
47
    {
48
        std::string osType{};
49
        bool bRequired = false;
50
    };
51

52
    std::map<std::string, OtherArgument> oOtherArguments{};
53

54
    //! Requested input data type.
55
    GDALDataType eRequestedInputDT = GDT_Unknown;
56

57
    //! List of supported input datatypes. Empty if no restriction.
58
    std::vector<GDALDataType> aeSupportedInputDT{};
59

60
    //! List of supported input band counts. Empty if no restriction.
61
    std::vector<int> anSupportedInputBandCount{};
62

63
    //! Optional initialization function
64
    GDALVRTProcessedDatasetFuncInit pfnInit = nullptr;
65

66
    //! Optional free function
67
    GDALVRTProcessedDatasetFuncFree pfnFree = nullptr;
68

69
    //! Required processing function
70
    GDALVRTProcessedDatasetFuncProcess pfnProcess = nullptr;
71
};
72

73
/************************************************************************/
74
/*                      GetGlobalMapProcessedDatasetFunc()              */
75
/************************************************************************/
76

77
/** Return the registry of VRTProcessedDatasetFunc functions */
78
static std::map<std::string, VRTProcessedDatasetFunc> &
79
GetGlobalMapProcessedDatasetFunc()
7,359✔
80
{
81
    static std::map<std::string, VRTProcessedDatasetFunc> goMap;
7,359✔
82
    return goMap;
7,359✔
83
}
84

85
/************************************************************************/
86
/*                            Step::~Step()                             */
87
/************************************************************************/
88

89
/*! @cond Doxygen_Suppress */
90

91
/** Step destructor */
92
VRTProcessedDataset::Step::~Step()
150✔
93
{
94
    deinit();
150✔
95
}
150✔
96

97
/************************************************************************/
98
/*                           Step::deinit()                             */
99
/************************************************************************/
100

101
/** Free pWorkingData */
102
void VRTProcessedDataset::Step::deinit()
150✔
103
{
104
    if (pWorkingData)
150✔
105
    {
106
        const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
58✔
107
        const auto oIterFunc = oMapFunctions.find(osAlgorithm);
58✔
108
        if (oIterFunc != oMapFunctions.end())
58✔
109
        {
110
            if (oIterFunc->second.pfnFree)
58✔
111
            {
112
                oIterFunc->second.pfnFree(osAlgorithm.c_str(),
116✔
113
                                          oIterFunc->second.pUserData,
58✔
114
                                          pWorkingData);
115
            }
116
        }
117
        else
118
        {
119
            CPLAssert(false);
×
120
        }
121
        pWorkingData = nullptr;
58✔
122
    }
123
}
150✔
124

125
/************************************************************************/
126
/*                        Step::Step(Step&& other)                      */
127
/************************************************************************/
128

129
/** Move constructor */
130
VRTProcessedDataset::Step::Step(Step &&other)
61✔
131
    : osAlgorithm(std::move(other.osAlgorithm)),
61✔
132
      aosArguments(std::move(other.aosArguments)), eInDT(other.eInDT),
122✔
133
      eOutDT(other.eOutDT), nInBands(other.nInBands),
61✔
134
      nOutBands(other.nOutBands), adfInNoData(other.adfInNoData),
61✔
135
      adfOutNoData(other.adfOutNoData), pWorkingData(other.pWorkingData)
61✔
136
{
137
    other.pWorkingData = nullptr;
61✔
138
}
61✔
139

140
/************************************************************************/
141
/*                      Step operator=(Step&& other)                    */
142
/************************************************************************/
143

144
/** Move assignment operator */
145
VRTProcessedDataset::Step &VRTProcessedDataset::Step::operator=(Step &&other)
×
146
{
147
    if (&other != this)
×
148
    {
149
        deinit();
×
150
        osAlgorithm = std::move(other.osAlgorithm);
×
151
        aosArguments = std::move(other.aosArguments);
×
152
        eInDT = other.eInDT;
×
153
        eOutDT = other.eOutDT;
×
154
        nInBands = other.nInBands;
×
155
        nOutBands = other.nOutBands;
×
156
        adfInNoData = std::move(other.adfInNoData);
×
157
        adfOutNoData = std::move(other.adfOutNoData);
×
158
        std::swap(pWorkingData, other.pWorkingData);
×
159
    }
160
    return *this;
×
161
}
162

163
/************************************************************************/
164
/*                        VRTProcessedDataset()                         */
165
/************************************************************************/
166

167
/** Constructor */
168
VRTProcessedDataset::VRTProcessedDataset(int nXSize, int nYSize)
99✔
169
    : VRTDataset(nXSize, nYSize)
99✔
170
{
171
}
99✔
172

173
/************************************************************************/
174
/*                       ~VRTProcessedDataset()                         */
175
/************************************************************************/
176

177
VRTProcessedDataset::~VRTProcessedDataset()
198✔
178

179
{
180
    VRTProcessedDataset::FlushCache(true);
99✔
181
    VRTProcessedDataset::CloseDependentDatasets();
99✔
182
}
198✔
183

184
/************************************************************************/
185
/*                              XMLInit()                               */
186
/************************************************************************/
187

188
/** Instantiate object from XML tree */
189
CPLErr VRTProcessedDataset::XMLInit(const CPLXMLNode *psTree,
96✔
190
                                    const char *pszVRTPathIn)
191

192
{
193
    if (Init(psTree, pszVRTPathIn, nullptr, nullptr, -1) != CE_None)
96✔
194
        return CE_Failure;
43✔
195

196
    const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
53✔
197
    const int nOvrCount = poSrcFirstBand->GetOverviewCount();
53✔
198
    for (int i = 0; i < nOvrCount; ++i)
56✔
199
    {
200
        auto poOvrDS = std::make_unique<VRTProcessedDataset>(0, 0);
3✔
201
        if (poOvrDS->Init(psTree, pszVRTPathIn, this, m_poSrcDS.get(), i) !=
3✔
202
            CE_None)
203
            break;
×
204
        m_apoOverviewDatasets.emplace_back(std::move(poOvrDS));
3✔
205
    }
206

207
    return CE_None;
53✔
208
}
209

210
static bool HasScaleOffset(GDALDataset &oSrcDS)
84✔
211
{
212
    for (int i = 1; i <= oSrcDS.GetRasterCount(); i++)
372✔
213
    {
214
        int pbSuccess;
215
        GDALRasterBand &oBand = *oSrcDS.GetRasterBand(i);
290✔
216
        double scale = oBand.GetScale(&pbSuccess);
290✔
217
        if (pbSuccess && scale != 1)
290✔
218
        {
219
            return true;
2✔
220
        }
221
        double offset = oBand.GetOffset(&pbSuccess);
288✔
222
        if (pbSuccess && offset != 0)
288✔
223
        {
224
            return true;
×
225
        }
226
    }
227

228
    return false;
82✔
229
}
230

231
/** Instantiate object from XML tree */
232
CPLErr VRTProcessedDataset::Init(const CPLXMLNode *psTree,
99✔
233
                                 const char *pszVRTPathIn,
234
                                 const VRTProcessedDataset *poParentDS,
235
                                 GDALDataset *poParentSrcDS, int iOvrLevel)
236

237
{
238
    const CPLXMLNode *psInput = CPLGetXMLNode(psTree, "Input");
99✔
239
    if (!psInput)
99✔
240
    {
241
        CPLError(CE_Failure, CPLE_AppDefined, "Input element missing");
1✔
242
        return CE_Failure;
1✔
243
    }
244

245
    if (pszVRTPathIn)
98✔
246
        m_osVRTPath = pszVRTPathIn;
12✔
247

248
    if (poParentSrcDS)
98✔
249
    {
250
        m_poSrcDS.reset(
3✔
251
            GDALCreateOverviewDataset(poParentSrcDS, iOvrLevel, true));
252
    }
253
    else if (const CPLXMLNode *psSourceFileNameNode =
95✔
254
                 CPLGetXMLNode(psInput, "SourceFilename"))
95✔
255
    {
256
        const bool bRelativeToVRT = CPL_TO_BOOL(
93✔
257
            atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0")));
258
        const std::string osFilename = GDALDataset::BuildFilename(
259
            CPLGetXMLValue(psInput, "SourceFilename", ""), pszVRTPathIn,
260
            bRelativeToVRT);
186✔
261
        m_poSrcDS.reset(GDALDataset::Open(
93✔
262
            osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
263
            nullptr, nullptr));
264
    }
265
    else if (const CPLXMLNode *psVRTDataset =
2✔
266
                 CPLGetXMLNode(psInput, "VRTDataset"))
2✔
267
    {
268
        CPLXMLNode sVRTDatasetTmp = *psVRTDataset;
1✔
269
        sVRTDatasetTmp.psNext = nullptr;
1✔
270
        char *pszXML = CPLSerializeXMLTree(&sVRTDatasetTmp);
1✔
271
        m_poSrcDS = VRTDataset::OpenXML(pszXML, pszVRTPathIn, GA_ReadOnly);
1✔
272
        CPLFree(pszXML);
1✔
273
    }
274
    else
275
    {
276
        CPLError(
1✔
277
            CE_Failure, CPLE_AppDefined,
278
            "Input element should have a SourceFilename or VRTDataset element");
279
        return CE_Failure;
1✔
280
    }
281

282
    if (!m_poSrcDS)
97✔
283
        return CE_Failure;
2✔
284

285
    const char *pszUnscale = CPLGetXMLValue(psInput, "unscale", "AUTO");
95✔
286
    bool bUnscale = false;
95✔
287
    if (EQUAL(pszUnscale, "AUTO"))
95✔
288
    {
289
        if (HasScaleOffset(*m_poSrcDS))
84✔
290
        {
291
            bUnscale = true;
2✔
292
        }
293
    }
294
    else if (EQUAL(pszUnscale, "YES") || EQUAL(pszUnscale, "ON") ||
11✔
295
             EQUAL(pszUnscale, "TRUE") || EQUAL(pszUnscale, "1"))
11✔
296
    {
297
        bUnscale = true;
6✔
298
    }
299
    else if (!(EQUAL(pszUnscale, "NO") || EQUAL(pszUnscale, "OFF") ||
5✔
300
               EQUAL(pszUnscale, "FALSE") || EQUAL(pszUnscale, "0")))
5✔
301
    {
302
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid value of 'unscale'");
1✔
303
        return CE_Failure;
1✔
304
    }
305

306
    if (bUnscale)
94✔
307
    {
308
        CPLStringList oArgs;
8✔
309
        oArgs.AddString("-unscale");
8✔
310
        oArgs.AddString("-ot");
8✔
311
        oArgs.AddString("Float64");
8✔
312
        oArgs.AddString("-of");
8✔
313
        oArgs.AddString("VRT");
8✔
314
        oArgs.AddString("-a_nodata");
8✔
315
        oArgs.AddString("nan");
8✔
316
        auto *poArgs = GDALTranslateOptionsNew(oArgs.List(), nullptr);
8✔
317
        int pbUsageError;
318
        CPLAssert(poArgs);
8✔
319
        m_poVRTSrcDS.reset(m_poSrcDS.release());
8✔
320
        // https://trac.cppcheck.net/ticket/11325
321
        // cppcheck-suppress accessMoved
322
        m_poSrcDS.reset(GDALDataset::FromHandle(
8✔
323
            GDALTranslate("", m_poVRTSrcDS.get(), poArgs, &pbUsageError)));
8✔
324
        GDALTranslateOptionsFree(poArgs);
8✔
325

326
        if (pbUsageError || !m_poSrcDS)
8✔
327
        {
328
            return CE_Failure;
×
329
        }
330
    }
331

332
    if (nRasterXSize == 0 && nRasterYSize == 0)
94✔
333
    {
334
        nRasterXSize = m_poSrcDS->GetRasterXSize();
92✔
335
        nRasterYSize = m_poSrcDS->GetRasterYSize();
92✔
336
    }
337
    else if (nRasterXSize != m_poSrcDS->GetRasterXSize() ||
2✔
338
             nRasterYSize != m_poSrcDS->GetRasterYSize())
×
339
    {
340
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
341
                 "Inconsistent declared VRT dimensions with input dataset");
342
        return CE_Failure;
2✔
343
    }
344

345
    if (m_poSrcDS->GetRasterCount() == 0)
92✔
346
        return CE_Failure;
×
347

348
    // Inherit SRS from source if not explicitly defined in VRT
349
    if (!CPLGetXMLNode(psTree, "SRS"))
92✔
350
    {
351
        const OGRSpatialReference *poSRS = m_poSrcDS->GetSpatialRef();
92✔
352
        if (poSRS)
92✔
353
        {
354
            m_poSRS.reset(poSRS->Clone());
10✔
355
        }
356
    }
357

358
    // Inherit GeoTransform from source if not explicitly defined in VRT
359
    if (iOvrLevel < 0 && !CPLGetXMLNode(psTree, "GeoTransform"))
92✔
360
    {
361
        if (m_poSrcDS->GetGeoTransform(m_gt) == CE_None)
89✔
362
            m_bGeoTransformSet = true;
52✔
363
    }
364

365
    /* -------------------------------------------------------------------- */
366
    /*      Initialize blocksize before calling sub-init so that the        */
367
    /*      band initializers can get it from the dataset object when       */
368
    /*      they are created.                                               */
369
    /* -------------------------------------------------------------------- */
370

371
    const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
92✔
372
    poSrcFirstBand->GetBlockSize(&m_nBlockXSize, &m_nBlockYSize);
92✔
373
    bool bUserBlockSize = false;
92✔
374
    if (const char *pszBlockXSize =
92✔
375
            CPLGetXMLValue(psTree, "BlockXSize", nullptr))
92✔
376
    {
377
        bUserBlockSize = true;
×
378
        m_nBlockXSize = atoi(pszBlockXSize);
×
379
        if (m_nBlockXSize <= 1)
×
380
        {
381
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockXSize");
×
382
            return CE_Failure;
×
383
        }
384
    }
385
    if (const char *pszBlockYSize =
92✔
386
            CPLGetXMLValue(psTree, "BlockYSize", nullptr))
92✔
387
    {
388
        bUserBlockSize = true;
×
389
        m_nBlockYSize = atoi(pszBlockYSize);
×
390
        if (m_nBlockYSize <= 1)
×
391
        {
392
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockYSize");
×
393
            return CE_Failure;
×
394
        }
395
    }
396

397
    // Initialize all the general VRT stuff.
398
    if (VRTDataset::XMLInit(psTree, pszVRTPathIn) != CE_None)
92✔
399
    {
400
        return CE_Failure;
×
401
    }
402

403
    // Use geotransform from parent for overviews
404
    if (iOvrLevel >= 0 && poParentDS->m_bGeoTransformSet)
92✔
405
    {
406
        m_bGeoTransformSet = true;
1✔
407
        m_gt = poParentDS->m_gt;
1✔
408
        m_gt[1] *=
2✔
409
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
1✔
410
        m_gt[2] *=
2✔
411
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
1✔
412
        m_gt[4] *=
2✔
413
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
1✔
414
        m_gt[5] *=
1✔
415
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
1✔
416
    }
417

418
    const CPLXMLNode *psOutputBands = CPLGetXMLNode(psTree, "OutputBands");
92✔
419
    if (psOutputBands)
92✔
420
    {
421
        if (const char *pszCount =
14✔
422
                CPLGetXMLValue(psOutputBands, "count", nullptr))
14✔
423
        {
424
            if (EQUAL(pszCount, "FROM_LAST_STEP"))
14✔
425
            {
426
                m_outputBandCountProvenance = ValueProvenance::FROM_LAST_STEP;
8✔
427
            }
428
            else if (!EQUAL(pszCount, "FROM_SOURCE"))
6✔
429
            {
430
                if (CPLGetValueType(pszCount) == CPL_VALUE_INTEGER)
4✔
431
                {
432
                    m_outputBandCountProvenance =
3✔
433
                        ValueProvenance::USER_PROVIDED;
434
                    m_outputBandCountValue = atoi(pszCount);
3✔
435
                    if (!GDALCheckBandCount(m_outputBandCountValue,
3✔
436
                                            /* bIsZeroAllowed = */ false))
437
                    {
438
                        // Error emitted by above function
439
                        return CE_Failure;
1✔
440
                    }
441
                }
442
                else
443
                {
444
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
445
                             "Invalid value for OutputBands.count");
446
                    return CE_Failure;
1✔
447
                }
448
            }
449
        }
450

451
        if (const char *pszType =
12✔
452
                CPLGetXMLValue(psOutputBands, "dataType", nullptr))
12✔
453
        {
454
            if (EQUAL(pszType, "FROM_LAST_STEP"))
12✔
455
            {
456
                m_outputBandDataTypeProvenance =
4✔
457
                    ValueProvenance::FROM_LAST_STEP;
458
            }
459
            else if (!EQUAL(pszType, "FROM_SOURCE"))
8✔
460
            {
461
                m_outputBandDataTypeProvenance = ValueProvenance::USER_PROVIDED;
6✔
462
                m_outputBandDataTypeValue = GDALGetDataTypeByName(pszType);
6✔
463
                if (m_outputBandDataTypeValue == GDT_Unknown)
6✔
464
                {
465
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
466
                             "Invalid value for OutputBands.dataType");
467
                    return CE_Failure;
1✔
468
                }
469
            }
470
        }
471
    }
472
    else if (CPLGetXMLNode(psTree, "VRTRasterBand"))
78✔
473
    {
474
        m_outputBandCountProvenance = ValueProvenance::FROM_VRTRASTERBAND;
17✔
475
        m_outputBandDataTypeProvenance = ValueProvenance::FROM_VRTRASTERBAND;
17✔
476
    }
477

478
    int nOutputBandCount = 0;
89✔
479
    switch (m_outputBandCountProvenance)
89✔
480
    {
481
        case ValueProvenance::USER_PROVIDED:
1✔
482
            nOutputBandCount = m_outputBandCountValue;
1✔
483
            break;
1✔
484
        case ValueProvenance::FROM_SOURCE:
63✔
485
            nOutputBandCount = m_poSrcDS->GetRasterCount();
63✔
486
            break;
63✔
487
        case ValueProvenance::FROM_VRTRASTERBAND:
17✔
488
            nOutputBandCount = nBands;
17✔
489
            break;
17✔
490
        case ValueProvenance::FROM_LAST_STEP:
8✔
491
            break;
8✔
492
    }
493

494
    const CPLXMLNode *psProcessingSteps =
495
        CPLGetXMLNode(psTree, "ProcessingSteps");
89✔
496
    if (!psProcessingSteps)
89✔
497
    {
498
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
499
                 "ProcessingSteps element missing");
500
        return CE_Failure;
1✔
501
    }
502

503
    const auto eInDT = poSrcFirstBand->GetRasterDataType();
88✔
504
    for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
298✔
505
    {
506
        const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
210✔
507
        if (eDT != eInDT)
210✔
508
        {
509
            CPLError(CE_Warning, CPLE_AppDefined,
×
510
                     "Not all bands of the input dataset have the same data "
511
                     "type. The data type of the first band will be used as "
512
                     "the reference one.");
513
            break;
×
514
        }
515
    }
516

517
    GDALDataType eCurrentDT = eInDT;
88✔
518
    int nCurrentBandCount = m_poSrcDS->GetRasterCount();
88✔
519

520
    std::vector<double> adfNoData;
176✔
521
    for (int i = 1; i <= nCurrentBandCount; ++i)
386✔
522
    {
523
        int bHasVal = FALSE;
298✔
524
        const double dfVal =
525
            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
298✔
526
        adfNoData.emplace_back(
527
            bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
298✔
528
    }
529

530
    int nStepCount = 0;
88✔
531
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
177✔
532
         psStep = psStep->psNext)
89✔
533
    {
534
        if (psStep->eType == CXT_Element &&
89✔
535
            strcmp(psStep->pszValue, "Step") == 0)
89✔
536
        {
537
            ++nStepCount;
89✔
538
        }
539
    }
540

541
    int iStep = 0;
88✔
542
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
146✔
543
         psStep = psStep->psNext)
58✔
544
    {
545
        if (psStep->eType == CXT_Element &&
89✔
546
            strcmp(psStep->pszValue, "Step") == 0)
89✔
547
        {
548
            ++iStep;
89✔
549
            const bool bIsFinalStep = (iStep == nStepCount);
89✔
550
            std::vector<double> adfOutNoData;
89✔
551
            if (bIsFinalStep)
89✔
552
            {
553
                // Initialize adfOutNoData with nodata value of *output* bands
554
                // for final step
555
                if (m_outputBandCountProvenance ==
87✔
556
                    ValueProvenance::FROM_VRTRASTERBAND)
557
                {
558
                    for (int i = 1; i <= nBands; ++i)
48✔
559
                    {
560
                        int bHasVal = FALSE;
31✔
561
                        const double dfVal =
562
                            GetRasterBand(i)->GetNoDataValue(&bHasVal);
31✔
563
                        adfOutNoData.emplace_back(
564
                            bHasVal ? dfVal
31✔
565
                                    : std::numeric_limits<double>::quiet_NaN());
31✔
566
                    }
567
                }
568
                else if (m_outputBandCountProvenance ==
70✔
569
                         ValueProvenance::FROM_SOURCE)
570
                {
571
                    for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
213✔
572
                    {
573
                        int bHasVal = FALSE;
152✔
574
                        const double dfVal =
575
                            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(
152✔
576
                                &bHasVal);
152✔
577
                        adfOutNoData.emplace_back(
578
                            bHasVal ? dfVal
182✔
579
                                    : std::numeric_limits<double>::quiet_NaN());
182✔
580
                    }
581
                }
582
                else if (m_outputBandCountProvenance ==
9✔
583
                         ValueProvenance::USER_PROVIDED)
584
                {
585
                    adfOutNoData.resize(
1✔
586
                        m_outputBandCountValue,
1✔
587
                        std::numeric_limits<double>::quiet_NaN());
1✔
588
                }
589
            }
590
            if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
89✔
591
                           adfNoData, adfOutNoData))
592
                return CE_Failure;
31✔
593
            adfNoData = std::move(adfOutNoData);
58✔
594
        }
595
    }
596

597
    if (m_aoSteps.empty())
57✔
598
    {
599
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
600
                 "At least one step should be defined");
601
        return CE_Failure;
1✔
602
    }
603

604
    int nLargestInDTSizeTimesBand = 1;
56✔
605
    int nLargestOutDTSizeTimesBand = 1;
56✔
606
    for (const auto &oStep : m_aoSteps)
114✔
607
    {
608
        const int nInDTSizeTimesBand =
609
            GDALGetDataTypeSizeBytes(oStep.eInDT) * oStep.nInBands;
58✔
610
        nLargestInDTSizeTimesBand =
58✔
611
            std::max(nLargestInDTSizeTimesBand, nInDTSizeTimesBand);
58✔
612
        const int nOutDTSizeTimesBand =
613
            GDALGetDataTypeSizeBytes(oStep.eOutDT) * oStep.nOutBands;
58✔
614
        nLargestOutDTSizeTimesBand =
58✔
615
            std::max(nLargestOutDTSizeTimesBand, nOutDTSizeTimesBand);
58✔
616
    }
617
    m_nWorkingBytesPerPixel =
56✔
618
        nLargestInDTSizeTimesBand + nLargestOutDTSizeTimesBand;
56✔
619

620
    // Use only up to 40% of RAM to acquire source bands and generate the output
621
    // buffer.
622
    m_nAllowedRAMUsage = CPLGetUsablePhysicalRAM() / 10 * 4;
56✔
623
    // Only for tests now
624
    const char *pszMAX_RAM = "VRT_PROCESSED_DATASET_ALLOWED_RAM_USAGE";
56✔
625
    if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
56✔
626
    {
627
        CPL_IGNORE_RET_VAL(
3✔
628
            CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
3✔
629
    }
630

631
    if (m_nAllowedRAMUsage > 0)
56✔
632
    {
633
        bool bBlockSizeModified = false;
56✔
634
        while ((m_nBlockXSize >= 2 || m_nBlockYSize >= 2) &&
88✔
635
               static_cast<GIntBig>(m_nBlockXSize) * m_nBlockYSize >
81✔
636
                   m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
81✔
637
        {
638
            if ((m_nBlockXSize == nRasterXSize ||
32✔
639
                 m_nBlockYSize >= m_nBlockXSize) &&
30✔
640
                m_nBlockYSize >= 2)
17✔
641
            {
642
                m_nBlockYSize /= 2;
16✔
643
            }
644
            else
645
            {
646
                m_nBlockXSize /= 2;
16✔
647
            }
648
            bBlockSizeModified = true;
32✔
649
        }
650
        if (bBlockSizeModified)
56✔
651
        {
652
            if (bUserBlockSize)
3✔
653
            {
654
                CPLError(
×
655
                    CE_Warning, CPLE_AppDefined,
656
                    "Reducing block size to %d x %d to avoid consuming too "
657
                    "much RAM",
658
                    m_nBlockXSize, m_nBlockYSize);
659
            }
660
            else
661
            {
662
                CPLDebug(
3✔
663
                    "VRT",
664
                    "Reducing block size to %d x %d to avoid consuming too "
665
                    "much RAM",
666
                    m_nBlockXSize, m_nBlockYSize);
667
            }
668
        }
669
    }
670

671
    if (m_outputBandCountProvenance == ValueProvenance::FROM_LAST_STEP)
56✔
672
    {
673
        nOutputBandCount = nCurrentBandCount;
7✔
674
    }
675
    else if (nOutputBandCount != nCurrentBandCount)
49✔
676
    {
677
        // Should not happen frequently as pixel init functions are expected
678
        // to validate that they can accept the number of output bands provided
679
        // to them
680
        CPLError(
×
681
            CE_Failure, CPLE_AppDefined,
682
            "Number of output bands of last step (%d) is not consistent with "
683
            "number of VRTProcessedRasterBand's (%d)",
684
            nCurrentBandCount, nBands);
685
        return CE_Failure;
×
686
    }
687

688
    if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
56✔
689
    {
690
        m_outputBandDataTypeValue = eCurrentDT;
3✔
691
    }
692

693
    const auto ClearBands = [this]()
34✔
694
    {
695
        for (int i = 0; i < nBands; ++i)
9✔
696
            delete papoBands[i];
1✔
697
        CPLFree(papoBands);
8✔
698
        papoBands = nullptr;
8✔
699
        nBands = 0;
8✔
700
    };
64✔
701

702
    if (nBands != 0 &&
73✔
703
        (nBands != nOutputBandCount ||
17✔
704
         (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP &&
16✔
705
          m_outputBandDataTypeValue != papoBands[0]->GetRasterDataType())))
1✔
706
    {
707
        ClearBands();
1✔
708
    }
709

710
    const auto GetOutputBandType = [this, eCurrentDT](GDALDataType eSourceDT)
176✔
711
    {
712
        if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
87✔
713
            return eCurrentDT;
3✔
714
        else if (m_outputBandDataTypeProvenance ==
84✔
715
                 ValueProvenance::USER_PROVIDED)
716
            return m_outputBandDataTypeValue;
5✔
717
        else
718
            return eSourceDT;
79✔
719
    };
56✔
720

721
    if (m_outputBandCountProvenance == ValueProvenance::FROM_SOURCE)
56✔
722
    {
723
        for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
112✔
724
        {
725
            const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
79✔
726
            const GDALDataType eOutputBandType =
727
                GetOutputBandType(poSrcBand->GetRasterDataType());
79✔
728
            auto poBand =
729
                new VRTProcessedRasterBand(this, i + 1, eOutputBandType);
79✔
730
            poBand->CopyCommonInfoFrom(poSrcBand);
79✔
731
            SetBand(i + 1, poBand);
79✔
732
        }
733
    }
734
    else if (m_outputBandCountProvenance != ValueProvenance::FROM_VRTRASTERBAND)
23✔
735
    {
736
        const GDALDataType eOutputBandType = GetOutputBandType(eInDT);
8✔
737

738
        bool bClearAndSetBands = true;
8✔
739
        if (nBands == nOutputBandCount)
8✔
740
        {
741
            bClearAndSetBands = false;
1✔
742
            for (int i = 0; i < nBands; ++i)
3✔
743
            {
744
                bClearAndSetBands =
2✔
745
                    bClearAndSetBands ||
2✔
746
                    !dynamic_cast<VRTProcessedRasterBand *>(papoBands[i]) ||
4✔
747
                    papoBands[i]->GetRasterDataType() != eOutputBandType;
2✔
748
            }
749
        }
750
        if (bClearAndSetBands)
8✔
751
        {
752
            ClearBands();
7✔
753
            for (int i = 0; i < nOutputBandCount; ++i)
32✔
754
            {
755
                SetBand(i + 1, std::make_unique<VRTProcessedRasterBand>(
25✔
756
                                   this, i + 1, eOutputBandType));
50✔
757
            }
758
        }
759
    }
760

761
    if (nBands > 1)
56✔
762
        SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
41✔
763

764
    m_oXMLTree.reset(CPLCloneXMLTree(psTree));
56✔
765

766
    return CE_None;
56✔
767
}
768

769
/************************************************************************/
770
/*                            ParseStep()                               */
771
/************************************************************************/
772

773
/** Parse the current Step node and create a corresponding entry in m_aoSteps.
774
 *
775
 * @param psStep Step node
776
 * @param bIsFinalStep Whether this is the final step.
777
 * @param[in,out] eCurrentDT Input data type for this step.
778
 *                           Updated to output data type at end of method.
779
 * @param[in,out] nCurrentBandCount Input band count for this step.
780
 *                                  Updated to output band cout at end of
781
 *                                  method.
782
 * @param adfInNoData Input nodata values
783
 * @param[in,out] adfOutNoData Output nodata values, to be filled by this
784
 *                             method. When bIsFinalStep, this is also an
785
 *                             input parameter.
786
 * @return true on success.
787
 */
788
bool VRTProcessedDataset::ParseStep(const CPLXMLNode *psStep, bool bIsFinalStep,
89✔
789
                                    GDALDataType &eCurrentDT,
790
                                    int &nCurrentBandCount,
791
                                    std::vector<double> &adfInNoData,
792
                                    std::vector<double> &adfOutNoData)
793
{
794
    const char *pszStepName = CPLGetXMLValue(
89✔
795
        psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
89✔
796
    const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
89✔
797
    if (!pszAlgorithm)
89✔
798
    {
799
        CPLError(CE_Failure, CPLE_AppDefined,
×
800
                 "Step '%s' lacks a Algorithm element", pszStepName);
801
        return false;
×
802
    }
803

804
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
89✔
805
    const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
89✔
806
    if (oIterFunc == oMapFunctions.end())
89✔
807
    {
808
        CPLError(CE_Failure, CPLE_AppDefined,
×
809
                 "Step '%s' uses unregistered algorithm '%s'", pszStepName,
810
                 pszAlgorithm);
811
        return false;
×
812
    }
813

814
    const auto &oFunc = oIterFunc->second;
89✔
815

816
    if (!oFunc.aeSupportedInputDT.empty())
89✔
817
    {
818
        if (std::find(oFunc.aeSupportedInputDT.begin(),
×
819
                      oFunc.aeSupportedInputDT.end(),
820
                      eCurrentDT) == oFunc.aeSupportedInputDT.end())
×
821
        {
822
            CPLError(CE_Failure, CPLE_AppDefined,
×
823
                     "Step '%s' (using algorithm '%s') does not "
824
                     "support input data type = '%s'",
825
                     pszStepName, pszAlgorithm,
826
                     GDALGetDataTypeName(eCurrentDT));
827
            return false;
×
828
        }
829
    }
830

831
    if (!oFunc.anSupportedInputBandCount.empty())
89✔
832
    {
833
        if (std::find(oFunc.anSupportedInputBandCount.begin(),
×
834
                      oFunc.anSupportedInputBandCount.end(),
835
                      nCurrentBandCount) ==
×
836
            oFunc.anSupportedInputBandCount.end())
×
837
        {
838
            CPLError(CE_Failure, CPLE_AppDefined,
×
839
                     "Step '%s' (using algorithm '%s') does not "
840
                     "support input band count = %d",
841
                     pszStepName, pszAlgorithm, nCurrentBandCount);
842
            return false;
×
843
        }
844
    }
845

846
    Step oStep;
178✔
847
    oStep.osAlgorithm = pszAlgorithm;
89✔
848
    oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
178✔
849
                      ? oFunc.eRequestedInputDT
89✔
850
                      : eCurrentDT;
851
    oStep.nInBands = nCurrentBandCount;
89✔
852

853
    // Unless modified by pfnInit...
854
    oStep.eOutDT = oStep.eInDT;
89✔
855

856
    oStep.adfInNoData = adfInNoData;
89✔
857
    oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
89✔
858

859
    // Deal with constant arguments
860
    for (const auto &nameValuePair : oFunc.oMapConstantArguments)
89✔
861
    {
862
        oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
863
                                        nameValuePair.second.c_str());
×
864
    }
865

866
    // Deal with built-in arguments
867
    if (oFunc.oSetBuiltinArguments.find("nodata") !=
89✔
868
        oFunc.oSetBuiltinArguments.end())
178✔
869
    {
870
        int bHasVal = false;
×
871
        const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
×
872
        const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
×
873
        if (bHasVal)
×
874
        {
875
            oStep.aosArguments.AddNameValue("nodata",
876
                                            CPLSPrintf("%.17g", dfVal));
×
877
        }
878
    }
879

880
    if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
89✔
881
        oFunc.oSetBuiltinArguments.end())
178✔
882
    {
883
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
×
884
        {
885
            int bHasVal = false;
×
886
            const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
×
887
            oStep.aosArguments.AddNameValue(
888
                CPLSPrintf("offset_%d", i),
889
                CPLSPrintf("%.17g", bHasVal ? dfVal : 0.0));
×
890
        }
891
    }
892

893
    if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
89✔
894
        oFunc.oSetBuiltinArguments.end())
178✔
895
    {
896
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
×
897
        {
898
            int bHasVal = false;
×
899
            const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
×
900
            oStep.aosArguments.AddNameValue(
901
                CPLSPrintf("scale_%d", i),
902
                CPLSPrintf("%.17g", bHasVal ? dfVal : 1.0));
×
903
        }
904
    }
905

906
    // Parse arguments specified in VRT
907
    std::set<std::string> oFoundArguments;
178✔
908

909
    for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
469✔
910
         psStepChild = psStepChild->psNext)
380✔
911
    {
912
        if (psStepChild->eType == CXT_Element &&
380✔
913
            strcmp(psStepChild->pszValue, "Argument") == 0)
356✔
914
        {
915
            const char *pszParamName =
916
                CPLGetXMLValue(psStepChild, "name", nullptr);
267✔
917
            if (!pszParamName)
267✔
918
            {
919
                CPLError(CE_Failure, CPLE_AppDefined,
×
920
                         "Step '%s' has a Argument without a name attribute",
921
                         pszStepName);
922
                return false;
×
923
            }
924
            const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
267✔
925
            auto oOtherArgIter =
926
                oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
267✔
927
            if (!oFunc.oOtherArguments.empty() &&
534✔
928
                oOtherArgIter == oFunc.oOtherArguments.end())
534✔
929
            {
930
                // If we got a parameter name like 'coefficients_1',
931
                // try to fetch the generic 'coefficients_{band}'
932
                std::string osParamName(pszParamName);
296✔
933
                const auto nPos = osParamName.rfind('_');
148✔
934
                if (nPos != std::string::npos)
148✔
935
                {
936
                    osParamName.resize(nPos + 1);
148✔
937
                    osParamName += "{band}";
148✔
938
                    oOtherArgIter = oFunc.oOtherArguments.find(
939
                        CPLString(osParamName).tolower());
148✔
940
                }
941
            }
942
            if (oOtherArgIter != oFunc.oOtherArguments.end())
267✔
943
            {
944
                oFoundArguments.insert(oOtherArgIter->first);
267✔
945

946
                const std::string &osType = oOtherArgIter->second.osType;
267✔
947
                if (osType == "boolean")
267✔
948
                {
949
                    if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
×
950
                    {
951
                        CPLError(CE_Failure, CPLE_NotSupported,
×
952
                                 "Step '%s' has a Argument '%s' whose "
953
                                 "value '%s' is not a boolean",
954
                                 pszStepName, pszParamName, pszValue);
955
                        return false;
×
956
                    }
957
                }
958
                else if (osType == "integer")
267✔
959
                {
960
                    if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
44✔
961
                    {
962
                        CPLError(CE_Failure, CPLE_NotSupported,
×
963
                                 "Step '%s' has a Argument '%s' whose "
964
                                 "value '%s' is not a integer",
965
                                 pszStepName, pszParamName, pszValue);
966
                        return false;
×
967
                    }
968
                }
969
                else if (osType == "double")
223✔
970
                {
971
                    const auto eType = CPLGetValueType(pszValue);
49✔
972
                    if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
49✔
973
                    {
974
                        CPLError(CE_Failure, CPLE_NotSupported,
×
975
                                 "Step '%s' has a Argument '%s' whose "
976
                                 "value '%s' is not a double",
977
                                 pszStepName, pszParamName, pszValue);
978
                        return false;
×
979
                    }
980
                }
981
                else if (osType == "double_list")
174✔
982
                {
983
                    const CPLStringList aosTokens(
984
                        CSLTokenizeString2(pszValue, ",", 0));
88✔
985
                    for (int i = 0; i < aosTokens.size(); ++i)
405✔
986
                    {
987
                        const auto eType = CPLGetValueType(aosTokens[i]);
317✔
988
                        if (eType != CPL_VALUE_INTEGER &&
317✔
989
                            eType != CPL_VALUE_REAL)
990
                        {
991
                            CPLError(CE_Failure, CPLE_NotSupported,
×
992
                                     "Step '%s' has a Argument '%s' "
993
                                     "whose value '%s' is not a "
994
                                     "comma-separated list of doubles",
995
                                     pszStepName, pszParamName, pszValue);
996
                            return false;
×
997
                        }
998
                    }
999
                }
1000
                else if (osType != "string")
86✔
1001
                {
1002
                    CPLDebug("VRT", "Unhandled argument type '%s'",
×
1003
                             osType.c_str());
1004
                    CPLAssert(0);
×
1005
                }
1006
            }
1007
            else if (oFunc.bMetadataSpecified &&
×
1008
                     oFunc.oSetBuiltinArguments.find(
×
1009
                         CPLString(pszParamName).tolower()) ==
×
1010
                         oFunc.oSetBuiltinArguments.end() &&
×
1011
                     oFunc.oMapConstantArguments.find(
×
1012
                         CPLString(pszParamName).tolower()) ==
×
1013
                         oFunc.oMapConstantArguments.end())
×
1014
            {
1015
                CPLError(CE_Warning, CPLE_NotSupported,
×
1016
                         "Step '%s' has a Argument '%s' which is not "
1017
                         "supported",
1018
                         pszStepName, pszParamName);
1019
            }
1020

1021
            oStep.aosArguments.AddNameValue(pszParamName, pszValue);
267✔
1022
        }
1023
    }
1024

1025
    // Check that required arguments have been specified
1026
    for (const auto &oIter : oFunc.oOtherArguments)
667✔
1027
    {
1028
        if (oIter.second.bRequired &&
741✔
1029
            oFoundArguments.find(oIter.first) == oFoundArguments.end())
741✔
1030
        {
1031
            CPLError(CE_Failure, CPLE_AppDefined,
3✔
1032
                     "Step '%s' lacks required Argument '%s'", pszStepName,
1033
                     oIter.first.c_str());
1034
            return false;
3✔
1035
        }
1036
    }
1037

1038
    if (oFunc.pfnInit)
86✔
1039
    {
1040
        double *padfOutNoData = nullptr;
86✔
1041
        if (bIsFinalStep && !adfOutNoData.empty())
86✔
1042
        {
1043
            oStep.nOutBands = static_cast<int>(adfOutNoData.size());
76✔
1044
            padfOutNoData = static_cast<double *>(
76✔
1045
                CPLMalloc(adfOutNoData.size() * sizeof(double)));
76✔
1046
            memcpy(padfOutNoData, adfOutNoData.data(),
76✔
1047
                   adfOutNoData.size() * sizeof(double));
76✔
1048
        }
1049
        else
1050
        {
1051
            oStep.nOutBands = 0;
10✔
1052
        }
1053

1054
        if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
172✔
1055
                          oStep.aosArguments.List(), oStep.nInBands,
86✔
1056
                          oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
1057
                          &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
1058
                          &(oStep.pWorkingData)) != CE_None)
86✔
1059
        {
1060
            CPLError(CE_Failure, CPLE_AppDefined,
28✔
1061
                     "Step '%s' (using algorithm '%s') init() function "
1062
                     "failed",
1063
                     pszStepName, pszAlgorithm);
1064
            CPLFree(padfOutNoData);
28✔
1065
            return false;
28✔
1066
        }
1067

1068
        // Input nodata values may have been modified by pfnInit()
1069
        oStep.adfInNoData = adfInNoData;
58✔
1070

1071
        if (padfOutNoData)
58✔
1072
        {
1073
            adfOutNoData = std::vector<double>(padfOutNoData,
108✔
1074
                                               padfOutNoData + oStep.nOutBands);
54✔
1075
        }
1076
        else
1077
        {
1078
            adfOutNoData = std::vector<double>(
12✔
1079
                oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
8✔
1080
        }
1081
        CPLFree(padfOutNoData);
58✔
1082

1083
        oStep.adfOutNoData = adfOutNoData;
58✔
1084
    }
1085
    else
1086
    {
1087
        oStep.nOutBands = oStep.nInBands;
×
1088
        adfOutNoData = oStep.adfOutNoData;
×
1089
    }
1090

1091
    eCurrentDT = oStep.eOutDT;
58✔
1092
    nCurrentBandCount = oStep.nOutBands;
58✔
1093

1094
    m_aoSteps.emplace_back(std::move(oStep));
58✔
1095

1096
    return true;
58✔
1097
}
1098

1099
/************************************************************************/
1100
/*                           SerializeToXML()                           */
1101
/************************************************************************/
1102

1103
CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
1✔
1104

1105
{
1106
    CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
1✔
1107
    if (psTree == nullptr)
1✔
1108
        return psTree;
×
1109

1110
    /* -------------------------------------------------------------------- */
1111
    /*      Remove VRTRasterBand nodes from the original tree and find the  */
1112
    /*      last child.                                                     */
1113
    /* -------------------------------------------------------------------- */
1114
    CPLXMLNode *psLastChild = psTree->psChild;
1✔
1115
    CPLXMLNode *psPrevChild = nullptr;
1✔
1116
    while (psLastChild)
5✔
1117
    {
1118
        CPLXMLNode *psNextChild = psLastChild->psNext;
5✔
1119
        if (psLastChild->eType == CXT_Element &&
5✔
1120
            strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
3✔
1121
        {
1122
            if (psPrevChild)
1✔
1123
                psPrevChild->psNext = psNextChild;
1✔
1124
            else
1125
                psTree->psChild = psNextChild;
×
1126
            psLastChild->psNext = nullptr;
1✔
1127
            CPLDestroyXMLNode(psLastChild);
1✔
1128
            psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
1✔
1129
        }
1130
        else if (!psNextChild)
4✔
1131
        {
1132
            break;
1✔
1133
        }
1134
        else
1135
        {
1136
            psPrevChild = psLastChild;
3✔
1137
            psLastChild = psNextChild;
3✔
1138
        }
1139
    }
1140
    CPLAssert(psLastChild);  // we have at least Input
1✔
1141

1142
    /* -------------------------------------------------------------------- */
1143
    /*      Serialize bands.                                                */
1144
    /* -------------------------------------------------------------------- */
1145
    bool bHasWarnedAboutRAMUsage = false;
1✔
1146
    size_t nAccRAMUsage = 0;
1✔
1147
    for (int iBand = 0; iBand < nBands; iBand++)
2✔
1148
    {
1149
        CPLXMLNode *psBandTree =
1150
            static_cast<VRTRasterBand *>(papoBands[iBand])
1✔
1151
                ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
2✔
1152
                                 nAccRAMUsage);
1✔
1153

1154
        if (psBandTree != nullptr)
1✔
1155
        {
1156
            psLastChild->psNext = psBandTree;
1✔
1157
            psLastChild = psBandTree;
1✔
1158
        }
1159
    }
1160

1161
    return psTree;
1✔
1162
}
1163

1164
/************************************************************************/
1165
/*                           SerializeToXML()                           */
1166
/************************************************************************/
1167

1168
CPLXMLNode *
1169
VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
1✔
1170
                                       bool &bHasWarnedAboutRAMUsage,
1171
                                       size_t &nAccRAMUsage)
1172

1173
{
1174
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1✔
1175
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1176

1177
    /* -------------------------------------------------------------------- */
1178
    /*      Set subclass.                                                   */
1179
    /* -------------------------------------------------------------------- */
1180
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1✔
1181
                     CXT_Text, "VRTProcessedRasterBand");
1182

1183
    return psTree;
1✔
1184
}
1185

1186
/************************************************************************/
1187
/*                            GetBlockSize()                            */
1188
/************************************************************************/
1189

1190
/** Return block size */
1191
void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
140✔
1192
                                       int *pnBlockYSize) const
1193

1194
{
1195
    *pnBlockXSize = m_nBlockXSize;
140✔
1196
    *pnBlockYSize = m_nBlockYSize;
140✔
1197
}
140✔
1198

1199
/************************************************************************/
1200
/*                            ProcessRegion()                           */
1201
/************************************************************************/
1202

1203
/** Compute pixel values for the specified region.
1204
 *
1205
 * The output is stored in m_abyInput in a pixel-interleaved way.
1206
 */
1207
bool VRTProcessedDataset::ProcessRegion(int nXOff, int nYOff, int nBufXSize,
70✔
1208
                                        int nBufYSize,
1209
                                        GDALProgressFunc pfnProgress,
1210
                                        void *pProgressData)
1211
{
1212

1213
    CPLAssert(!m_aoSteps.empty());
70✔
1214

1215
    const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
70✔
1216

1217
    const int nFirstBandCount = m_aoSteps.front().nInBands;
70✔
1218
    CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
70✔
1219
    const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
70✔
1220
    const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
70✔
1221
    auto &abyInput = m_abyInput;
70✔
1222
    auto &abyOutput = m_abyOutput;
70✔
1223

1224
    const char *pszInterleave =
1225
        m_poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
70✔
1226
    if (nFirstBandCount > 1 && (!pszInterleave || EQUAL(pszInterleave, "BAND")))
70✔
1227
    {
1228
        // If there are several bands and the source dataset organization
1229
        // is apparently band interleaved, then first acquire data in
1230
        // a BSQ organization in the abyInput array use in the native
1231
        // data type.
1232
        // And then transpose it and convert it to the expected data type
1233
        // of the first step.
1234
        const auto eSrcDT = m_poSrcDS->GetRasterBand(1)->GetRasterDataType();
1✔
1235
        try
1236
        {
1237
            abyInput.resize(nPixelCount * nFirstBandCount *
2✔
1238
                            GDALGetDataTypeSizeBytes(eSrcDT));
1✔
1239
        }
1240
        catch (const std::bad_alloc &)
×
1241
        {
1242
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1243
                     "Out of memory allocating working buffer");
1244
            return false;
×
1245
        }
1246

1247
        try
1248
        {
1249
            abyOutput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1✔
1250
        }
1251
        catch (const std::bad_alloc &)
×
1252
        {
1253
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1254
                     "Out of memory allocating working buffer");
1255
            return false;
×
1256
        }
1257

1258
        GDALRasterIOExtraArg sArg;
1259
        INIT_RASTERIO_EXTRA_ARG(sArg);
1✔
1260
        sArg.pfnProgress = GDALScaledProgress;
1✔
1261
        sArg.pProgressData =
1✔
1262
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1✔
1263
        if (sArg.pProgressData == nullptr)
1✔
1264
            sArg.pfnProgress = nullptr;
1✔
1265

1266
        CPLDebugOnly("VRT", "ProcessRegion(): start RasterIO()");
1✔
1267
        const bool bOK =
1268
            m_poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nBufXSize, nBufYSize,
2✔
1269
                                abyInput.data(), nBufXSize, nBufYSize, eSrcDT,
1✔
1270
                                nFirstBandCount, nullptr, 0, 0, 0,
1271
                                &sArg) == CE_None;
1✔
1272
        CPLDebugOnly("VRT", "ProcessRegion(): end RasterIO()");
1✔
1273
        GDALDestroyScaledProgress(sArg.pProgressData);
1✔
1274
        if (!bOK)
1✔
1275
            return false;
×
1276

1277
        CPLDebugOnly("VRT", "ProcessRegion(): start GDALTranspose2D()");
1✔
1278
        GDALTranspose2D(abyInput.data(), eSrcDT, abyOutput.data(), eFirstDT,
1✔
1279
                        static_cast<size_t>(nBufXSize) * nBufYSize,
1✔
1280
                        nFirstBandCount);
1281
        CPLDebugOnly("VRT", "ProcessRegion(): end GDALTranspose2D()");
1✔
1282

1283
        // Swap arrays
1284
        std::swap(abyInput, abyOutput);
1✔
1285
    }
1286
    else
1287
    {
1288
        try
1289
        {
1290
            abyInput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
69✔
1291
        }
1292
        catch (const std::bad_alloc &)
×
1293
        {
1294
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1295
                     "Out of memory allocating working buffer");
1296
            return false;
×
1297
        }
1298

1299
        GDALRasterIOExtraArg sArg;
1300
        INIT_RASTERIO_EXTRA_ARG(sArg);
69✔
1301
        sArg.pfnProgress = GDALScaledProgress;
69✔
1302
        sArg.pProgressData =
69✔
1303
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
69✔
1304
        if (sArg.pProgressData == nullptr)
69✔
1305
            sArg.pfnProgress = nullptr;
69✔
1306

1307
        const bool bOK =
1308
            m_poSrcDS->RasterIO(
138✔
1309
                GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
69✔
1310
                nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
1311
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
69✔
1312
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount *
69✔
1313
                    nBufXSize,
69✔
1314
                nFirstDTSize, &sArg) == CE_None;
69✔
1315

1316
        GDALDestroyScaledProgress(sArg.pProgressData);
69✔
1317
        if (!bOK)
69✔
1318
            return false;
3✔
1319
    }
1320

1321
    const double dfSrcXOff = nXOff;
67✔
1322
    const double dfSrcYOff = nYOff;
67✔
1323
    const double dfSrcXSize = nBufXSize;
67✔
1324
    const double dfSrcYSize = nBufYSize;
67✔
1325

1326
    GDALGeoTransform srcGT;
67✔
1327
    if (m_poSrcDS->GetGeoTransform(srcGT) != CE_None)
67✔
1328
    {
1329
        srcGT = GDALGeoTransform();
38✔
1330
    }
1331

1332
    GDALDataType eLastDT = eFirstDT;
67✔
1333
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
67✔
1334

1335
    int iStep = 0;
67✔
1336
    for (const auto &oStep : m_aoSteps)
132✔
1337
    {
1338
        const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
69✔
1339
        CPLAssert(oIterFunc != oMapFunctions.end());
69✔
1340

1341
        // Data type adaptation
1342
        if (eLastDT != oStep.eInDT)
69✔
1343
        {
1344
            try
1345
            {
1346
                abyOutput.resize(nPixelCount * oStep.nInBands *
×
1347
                                 GDALGetDataTypeSizeBytes(oStep.eInDT));
×
1348
            }
1349
            catch (const std::bad_alloc &)
×
1350
            {
1351
                CPLError(CE_Failure, CPLE_OutOfMemory,
×
1352
                         "Out of memory allocating working buffer");
1353
                return false;
×
1354
            }
1355

1356
            GDALCopyWords64(abyInput.data(), eLastDT,
×
1357
                            GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
×
1358
                            oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
×
1359
                            nPixelCount * oStep.nInBands);
×
1360

1361
            std::swap(abyInput, abyOutput);
×
1362
        }
1363

1364
        try
1365
        {
1366
            abyOutput.resize(nPixelCount * oStep.nOutBands *
138✔
1367
                             GDALGetDataTypeSizeBytes(oStep.eOutDT));
69✔
1368
        }
1369
        catch (const std::bad_alloc &)
×
1370
        {
1371
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1372
                     "Out of memory allocating working buffer");
1373
            return false;
×
1374
        }
1375

1376
        const auto &oFunc = oIterFunc->second;
69✔
1377
        if (oFunc.pfnProcess(
345✔
1378
                oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
69✔
1379
                oStep.aosArguments.List(), nBufXSize, nBufYSize,
1380
                abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
69✔
1381
                oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
69✔
1382
                oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
69✔
1383
                dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, srcGT.data(),
69✔
1384
                m_osVRTPath.c_str(),
1385
                /*papszExtra=*/nullptr) != CE_None)
69✔
1386
        {
1387
            return false;
4✔
1388
        }
1389

1390
        std::swap(abyInput, abyOutput);
65✔
1391
        eLastDT = oStep.eOutDT;
65✔
1392

1393
        ++iStep;
65✔
1394
        if (pfnProgress &&
65✔
1395
            !pfnProgress(0.5 + 0.5 * iStep / static_cast<int>(m_aoSteps.size()),
×
1396
                         "", pProgressData))
1397
            return false;
×
1398
    }
1399

1400
    return true;
63✔
1401
}
1402

1403
/************************************************************************/
1404
/*                        VRTProcessedRasterBand()                      */
1405
/************************************************************************/
1406

1407
/** Constructor */
1408
VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
140✔
1409
                                               int nBandIn,
1410
                                               GDALDataType eDataTypeIn)
140✔
1411
{
1412
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
140✔
1413

1414
    poDS = poDSIn;
140✔
1415
    nBand = nBandIn;
140✔
1416
    eAccess = GA_Update;
140✔
1417
    eDataType = eDataTypeIn;
140✔
1418

1419
    poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
140✔
1420
}
140✔
1421

1422
/************************************************************************/
1423
/*                           GetOverviewCount()                         */
1424
/************************************************************************/
1425

1426
int VRTProcessedRasterBand::GetOverviewCount()
6✔
1427
{
1428
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
6✔
1429
    return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
6✔
1430
}
1431

1432
/************************************************************************/
1433
/*                              GetOverview()                           */
1434
/************************************************************************/
1435

1436
GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
11✔
1437
{
1438
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
11✔
1439
    if (iOvr < 0 ||
22✔
1440
        iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
11✔
1441
        return nullptr;
×
1442
    return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
11✔
1443
}
1444

1445
/************************************************************************/
1446
/*                             IReadBlock()                             */
1447
/************************************************************************/
1448

1449
CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
45✔
1450
                                          void *pImage)
1451

1452
{
1453
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
45✔
1454

1455
    int nBufXSize = 0;
45✔
1456
    int nBufYSize = 0;
45✔
1457
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
45✔
1458

1459
    const int nXPixelOff = nBlockXOff * nBlockXSize;
45✔
1460
    const int nYPixelOff = nBlockYOff * nBlockYSize;
45✔
1461
    if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize,
45✔
1462
                                nullptr, nullptr))
1463
    {
1464
        return CE_Failure;
3✔
1465
    }
1466

1467
    const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
42✔
1468
    CPLAssert(nOutBands == poVRTDS->GetRasterCount());
42✔
1469
    const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
42✔
1470
    const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
42✔
1471
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
42✔
1472

1473
    // Dispatch final output buffer to cached blocks of output bands
1474
    for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
120✔
1475
    {
1476
        GDALRasterBlock *poBlock = nullptr;
78✔
1477
        GByte *pDst;
1478
        if (iDstBand + 1 == nBand)
78✔
1479
        {
1480
            pDst = static_cast<GByte *>(pImage);
42✔
1481
        }
1482
        else
1483
        {
1484
            auto poOtherBand = poVRTDS->papoBands[iDstBand];
36✔
1485
            poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
36✔
1486
            if (poBlock)
36✔
1487
            {
1488
                poBlock->DropLock();
×
1489
                continue;
×
1490
            }
1491
            poBlock = poOtherBand->GetLockedBlockRef(
72✔
1492
                nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
36✔
1493
            if (!poBlock)
36✔
1494
                continue;
×
1495
            pDst = static_cast<GByte *>(poBlock->GetDataRef());
36✔
1496
        }
1497
        for (int iY = 0; iY < nBufYSize; ++iY)
853✔
1498
        {
1499
            GDALCopyWords64(poVRTDS->m_abyInput.data() +
1,550✔
1500
                                (iDstBand + static_cast<size_t>(iY) *
775✔
1501
                                                nBufXSize * nOutBands) *
775✔
1502
                                    nLastDTSize,
775✔
1503
                            eLastDT, nLastDTSize * nOutBands,
1504
                            pDst +
775✔
1505
                                static_cast<size_t>(iY) * nBlockXSize * nDTSize,
775✔
1506
                            eDataType, nDTSize, nBufXSize);
1507
        }
1508
        if (poBlock)
78✔
1509
            poBlock->DropLock();
36✔
1510
    }
1511

1512
    return CE_None;
42✔
1513
}
1514

1515
/************************************************************************/
1516
/*                VRTProcessedDataset::IRasterIO()                      */
1517
/************************************************************************/
1518

1519
CPLErr VRTProcessedDataset::IRasterIO(
56✔
1520
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1521
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1522
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1523
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1524
{
1525
    // Try to pass the request to the most appropriate overview dataset.
1526
    if (nBufXSize < nXSize && nBufYSize < nYSize)
56✔
1527
    {
1528
        int bTried = FALSE;
2✔
1529
        const CPLErr eErr = TryOverviewRasterIO(
2✔
1530
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1531
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1532
            nBandSpace, psExtraArg, &bTried);
1533
        if (bTried)
2✔
1534
            return eErr;
2✔
1535
    }
1536

1537
    // Optimize reading of all bands at nominal resolution for BIP-like or
1538
    // BSQ-like buffer spacing.
1539
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
54✔
1540
        nBandCount == nBands)
53✔
1541
    {
1542
        const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
52✔
1543
        const bool bIsBIPLike = nBandSpace == nBufTypeSize &&
61✔
1544
                                nPixelSpace == nBandSpace * nBands &&
9✔
1545
                                nLineSpace >= nPixelSpace * nBufXSize &&
66✔
1546
                                IsAllBands(nBandCount, panBandMap);
5✔
1547
        const bool bIsBSQLike = nPixelSpace == nBufTypeSize &&
99✔
1548
                                nLineSpace >= nPixelSpace * nBufXSize &&
47✔
1549
                                nBandSpace >= nLineSpace * nBufYSize &&
146✔
1550
                                IsAllBands(nBandCount, panBandMap);
47✔
1551
        if (bIsBIPLike || bIsBSQLike)
52✔
1552
        {
1553
            GByte *pabyData = static_cast<GByte *>(pData);
50✔
1554
            // If acquiring the region of interest in a single time is going
1555
            // to consume too much RAM, split in halves.
1556
            if (m_nAllowedRAMUsage > 0 &&
50✔
1557
                static_cast<GIntBig>(nBufXSize) * nBufYSize >
50✔
1558
                    m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
50✔
1559
            {
1560
                if ((nBufXSize == nRasterXSize || nBufYSize >= nBufXSize) &&
25✔
1561
                    nBufYSize >= 2)
1562
                {
1563
                    GDALRasterIOExtraArg sArg;
1564
                    INIT_RASTERIO_EXTRA_ARG(sArg);
12✔
1565
                    const int nHalfHeight = nBufYSize / 2;
12✔
1566

1567
                    sArg.pfnProgress = GDALScaledProgress;
12✔
1568
                    sArg.pProgressData = GDALCreateScaledProgress(
12✔
1569
                        0, 0.5, psExtraArg->pfnProgress,
1570
                        psExtraArg->pProgressData);
1571
                    if (sArg.pProgressData == nullptr)
12✔
1572
                        sArg.pfnProgress = nullptr;
12✔
1573
                    bool bOK =
1574
                        IRasterIO(eRWFlag, nXOff, nYOff, nBufXSize, nHalfHeight,
12✔
1575
                                  pabyData, nBufXSize, nHalfHeight, eBufType,
1576
                                  nBandCount, panBandMap, nPixelSpace,
1577
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
12✔
1578
                    GDALDestroyScaledProgress(sArg.pProgressData);
12✔
1579

1580
                    if (bOK)
12✔
1581
                    {
1582
                        sArg.pfnProgress = GDALScaledProgress;
2✔
1583
                        sArg.pProgressData = GDALCreateScaledProgress(
2✔
1584
                            0.5, 1, psExtraArg->pfnProgress,
1585
                            psExtraArg->pProgressData);
1586
                        if (sArg.pProgressData == nullptr)
2✔
1587
                            sArg.pfnProgress = nullptr;
2✔
1588
                        bOK = IRasterIO(eRWFlag, nXOff, nYOff + nHalfHeight,
4✔
1589
                                        nBufXSize, nBufYSize - nHalfHeight,
1590
                                        pabyData + nHalfHeight * nLineSpace,
2✔
1591
                                        nBufXSize, nBufYSize - nHalfHeight,
1592
                                        eBufType, nBandCount, panBandMap,
1593
                                        nPixelSpace, nLineSpace, nBandSpace,
1594
                                        &sArg) == CE_None;
1595
                        GDALDestroyScaledProgress(sArg.pProgressData);
2✔
1596
                    }
1597
                    return bOK ? CE_None : CE_Failure;
12✔
1598
                }
1599
                else if (nBufXSize >= 2)
13✔
1600
                {
1601
                    GDALRasterIOExtraArg sArg;
1602
                    INIT_RASTERIO_EXTRA_ARG(sArg);
13✔
1603
                    const int nHalfWidth = nBufXSize / 2;
13✔
1604

1605
                    sArg.pfnProgress = GDALScaledProgress;
13✔
1606
                    sArg.pProgressData = GDALCreateScaledProgress(
13✔
1607
                        0, 0.5, psExtraArg->pfnProgress,
1608
                        psExtraArg->pProgressData);
1609
                    if (sArg.pProgressData == nullptr)
13✔
1610
                        sArg.pfnProgress = nullptr;
13✔
1611
                    bool bOK =
1612
                        IRasterIO(eRWFlag, nXOff, nYOff, nHalfWidth, nBufYSize,
13✔
1613
                                  pabyData, nHalfWidth, nBufYSize, eBufType,
1614
                                  nBandCount, panBandMap, nPixelSpace,
1615
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
13✔
1616
                    GDALDestroyScaledProgress(sArg.pProgressData);
13✔
1617

1618
                    if (bOK)
13✔
1619
                    {
1620
                        sArg.pfnProgress = GDALScaledProgress;
3✔
1621
                        sArg.pProgressData = GDALCreateScaledProgress(
3✔
1622
                            0.5, 1, psExtraArg->pfnProgress,
1623
                            psExtraArg->pProgressData);
1624
                        if (sArg.pProgressData == nullptr)
3✔
1625
                            sArg.pfnProgress = nullptr;
3✔
1626
                        bOK = IRasterIO(eRWFlag, nXOff + nHalfWidth, nYOff,
6✔
1627
                                        nBufXSize - nHalfWidth, nBufYSize,
1628
                                        pabyData + nHalfWidth * nPixelSpace,
3✔
1629
                                        nBufXSize - nHalfWidth, nBufYSize,
1630
                                        eBufType, nBandCount, panBandMap,
1631
                                        nPixelSpace, nLineSpace, nBandSpace,
1632
                                        &sArg) == CE_None;
1633
                        GDALDestroyScaledProgress(sArg.pProgressData);
3✔
1634
                    }
1635
                    return bOK ? CE_None : CE_Failure;
13✔
1636
                }
1637
            }
1638

1639
            if (!ProcessRegion(nXOff, nYOff, nBufXSize, nBufYSize,
25✔
1640
                               psExtraArg->pfnProgress,
1641
                               psExtraArg->pProgressData))
1642
            {
1643
                return CE_Failure;
4✔
1644
            }
1645
            const auto eLastDT = m_aoSteps.back().eOutDT;
21✔
1646
            const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
21✔
1647
            if (bIsBIPLike)
21✔
1648
            {
1649
                for (int iY = 0; iY < nBufYSize; ++iY)
10✔
1650
                {
1651
                    GDALCopyWords64(
21✔
1652
                        m_abyInput.data() + static_cast<size_t>(iY) * nBands *
14✔
1653
                                                nBufXSize * nLastDTSize,
7✔
1654
                        eLastDT, nLastDTSize, pabyData + iY * nLineSpace,
7✔
1655
                        eBufType, GDALGetDataTypeSizeBytes(eBufType),
1656
                        static_cast<size_t>(nBufXSize) * nBands);
7✔
1657
                }
1658
            }
1659
            else
1660
            {
1661
                CPLAssert(bIsBSQLike);
18✔
1662
                for (int iBand = 0; iBand < nBands; ++iBand)
92✔
1663
                {
1664
                    for (int iY = 0; iY < nBufYSize; ++iY)
168✔
1665
                    {
1666
                        GDALCopyWords64(
188✔
1667
                            m_abyInput.data() +
188✔
1668
                                (static_cast<size_t>(iY) * nBands * nBufXSize +
94✔
1669
                                 iBand) *
94✔
1670
                                    nLastDTSize,
94✔
1671
                            eLastDT, nLastDTSize * nBands,
94✔
1672
                            pabyData + iBand * nBandSpace + iY * nLineSpace,
94✔
1673
                            eBufType, nBufTypeSize, nBufXSize);
1674
                    }
1675
                }
1676
            }
1677
            return CE_None;
21✔
1678
        }
1679
    }
1680

1681
    return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
4✔
1682
                                 nBufXSize, nBufYSize, eBufType, nBandCount,
1683
                                 panBandMap, nPixelSpace, nLineSpace,
1684
                                 nBandSpace, psExtraArg);
4✔
1685
}
1686

1687
/*! @endcond */
1688

1689
/************************************************************************/
1690
/*                GDALVRTRegisterProcessedDatasetFunc()                 */
1691
/************************************************************************/
1692

1693
/** Register a function to be used by VRTProcessedDataset.
1694

1695
 An example of content for pszXMLMetadata is:
1696
 \verbatim
1697
  <ProcessedDatasetFunctionArgumentsList>
1698
     <Argument name='src_nodata' type='double' description='Override input nodata value'/>
1699
     <Argument name='dst_nodata' type='double' description='Override output nodata value'/>
1700
     <Argument name='replacement_nodata' description='value to substitute to a valid computed value that would be nodata' type='double'/>
1701
     <Argument name='dst_intended_datatype' type='string' description='Intented datatype of output (which might be different than the working data type)'/>
1702
     <Argument name='coefficients_{band}' description='Comma-separated coefficients for combining bands. First one is constant term' type='double_list' required='true'/>
1703
  </ProcessedDatasetFunctionArgumentsList>
1704
 \endverbatim
1705

1706
 @param pszFuncName Function name. Must be unique and not null.
1707
 @param pUserData User data. May be nullptr. Must remain valid during the
1708
                  lifetime of GDAL.
1709
 @param pszXMLMetadata XML metadata describing the function arguments. May be
1710
                       nullptr if there are no arguments.
1711
 @param eRequestedInputDT If the pfnProcess callback only supports a single
1712
                          data type, it should be specified in this parameter.
1713
                          Otherwise set it to GDT_Unknown.
1714
 @param paeSupportedInputDT List of supported input data types. May be nullptr
1715
                            if all are supported or if eRequestedInputDT is
1716
                            set to a non GDT_Unknown value.
1717
 @param nSupportedInputDTSize Size of paeSupportedInputDT
1718
 @param panSupportedInputBandCount List of supported band count. May be nullptr
1719
                                   if any source band count is supported.
1720
 @param nSupportedInputBandCountSize Size of panSupportedInputBandCount
1721
 @param pfnInit Initialization function called when a VRTProcessedDataset
1722
                step uses the register function. This initialization function
1723
                will return the output data type, output band count and
1724
                potentially initialize a working structure, typically parsing
1725
                arguments. May be nullptr.
1726
                If not specified, it will be assumed that the input and output
1727
                data types are the same, and that the input number of bands
1728
                and output number of bands are the same.
1729
 @param pfnFree Free function that will free the working structure allocated
1730
                by pfnInit. May be nullptr.
1731
 @param pfnProcess Processing function called to compute pixel values. Must
1732
                   not be nullptr.
1733
 @param papszOptions Unused currently. Must be nullptr.
1734
 @return CE_None in case of success, error otherwise.
1735
 @since 3.9
1736
 */
1737
CPLErr GDALVRTRegisterProcessedDatasetFunc(
7,145✔
1738
    const char *pszFuncName, void *pUserData, const char *pszXMLMetadata,
1739
    GDALDataType eRequestedInputDT, const GDALDataType *paeSupportedInputDT,
1740
    size_t nSupportedInputDTSize, const int *panSupportedInputBandCount,
1741
    size_t nSupportedInputBandCountSize,
1742
    GDALVRTProcessedDatasetFuncInit pfnInit,
1743
    GDALVRTProcessedDatasetFuncFree pfnFree,
1744
    GDALVRTProcessedDatasetFuncProcess pfnProcess,
1745
    CPL_UNUSED CSLConstList papszOptions)
1746
{
1747
    if (pszFuncName == nullptr || pszFuncName[0] == '\0')
7,145✔
1748
    {
1749
        CPLError(CE_Failure, CPLE_AppDefined,
×
1750
                 "pszFuncName should be non-empty");
1751
        return CE_Failure;
×
1752
    }
1753

1754
    auto &oMap = GetGlobalMapProcessedDatasetFunc();
7,145✔
1755
    if (oMap.find(pszFuncName) != oMap.end())
7,145✔
1756
    {
1757
        CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
×
1758
                 pszFuncName);
1759
        return CE_Failure;
×
1760
    }
1761

1762
    if (!pfnProcess)
7,145✔
1763
    {
1764
        CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
×
1765
        return CE_Failure;
×
1766
    }
1767

1768
    VRTProcessedDatasetFunc oFunc;
14,290✔
1769
    oFunc.osFuncName = pszFuncName;
7,145✔
1770
    oFunc.pUserData = pUserData;
7,145✔
1771
    if (pszXMLMetadata)
7,145✔
1772
    {
1773
        oFunc.bMetadataSpecified = true;
7,145✔
1774
        auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
7,145✔
1775
        if (!psTree)
7,145✔
1776
        {
1777
            CPLError(CE_Failure, CPLE_AppDefined,
×
1778
                     "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
1779
                     pszFuncName);
1780
            return CE_Failure;
×
1781
        }
1782
        const CPLXMLNode *psRoot = CPLGetXMLNode(
7,145✔
1783
            psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
1784
        if (!psRoot)
7,145✔
1785
        {
1786
            CPLError(CE_Failure, CPLE_AppDefined,
×
1787
                     "No root ProcessedDatasetFunctionArgumentsList element in "
1788
                     "pszXMLMetadata=%s for %s",
1789
                     pszXMLMetadata, pszFuncName);
1790
            return CE_Failure;
×
1791
        }
1792
        for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
54,302✔
1793
             psIter = psIter->psNext)
47,157✔
1794
        {
1795
            if (psIter->eType == CXT_Element &&
47,157✔
1796
                strcmp(psIter->pszValue, "Argument") == 0)
47,157✔
1797
            {
1798
                const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
47,157✔
1799
                if (!pszName)
47,157✔
1800
                {
1801
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1802
                             "Missing Argument.name attribute in "
1803
                             "pszXMLMetadata=%s for %s",
1804
                             pszXMLMetadata, pszFuncName);
1805
                    return CE_Failure;
×
1806
                }
1807
                const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
47,157✔
1808
                if (!pszType)
47,157✔
1809
                {
1810
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1811
                             "Missing Argument.type attribute in "
1812
                             "pszXMLMetadata=%s for %s",
1813
                             pszXMLMetadata, pszFuncName);
1814
                    return CE_Failure;
×
1815
                }
1816
                if (strcmp(pszType, "constant") == 0)
47,157✔
1817
                {
1818
                    const char *pszValue =
1819
                        CPLGetXMLValue(psIter, "value", nullptr);
×
1820
                    if (!pszValue)
×
1821
                    {
1822
                        CPLError(CE_Failure, CPLE_AppDefined,
×
1823
                                 "Missing Argument.value attribute in "
1824
                                 "pszXMLMetadata=%s for %s",
1825
                                 pszXMLMetadata, pszFuncName);
1826
                        return CE_Failure;
×
1827
                    }
1828
                    oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
×
1829
                        pszValue;
×
1830
                }
1831
                else if (strcmp(pszType, "builtin") == 0)
47,157✔
1832
                {
1833
                    if (EQUAL(pszName, "nodata") ||
×
1834
                        EQUAL(pszName, "offset_{band}") ||
×
1835
                        EQUAL(pszName, "scale_{band}"))
×
1836
                    {
1837
                        oFunc.oSetBuiltinArguments.insert(
×
1838
                            CPLString(pszName).tolower());
×
1839
                    }
1840
                    else
1841
                    {
1842
                        CPLError(CE_Failure, CPLE_NotSupported,
×
1843
                                 "Unsupported builtin parameter name %s in "
1844
                                 "pszXMLMetadata=%s for %s. Only nodata, "
1845
                                 "offset_{band} and scale_{band} are supported",
1846
                                 pszName, pszXMLMetadata, pszFuncName);
1847
                        return CE_Failure;
×
1848
                    }
1849
                }
1850
                else if (strcmp(pszType, "boolean") == 0 ||
47,157✔
1851
                         strcmp(pszType, "string") == 0 ||
44,299✔
1852
                         strcmp(pszType, "integer") == 0 ||
34,296✔
1853
                         strcmp(pszType, "double") == 0 ||
25,722✔
1854
                         strcmp(pszType, "double_list") == 0)
1,429✔
1855
                {
1856
                    VRTProcessedDatasetFunc::OtherArgument otherArgument;
47,157✔
1857
                    otherArgument.bRequired = CPLTestBool(
47,157✔
1858
                        CPLGetXMLValue(psIter, "required", "false"));
1859
                    otherArgument.osType = pszType;
47,157✔
1860
                    oFunc.oOtherArguments[CPLString(pszName).tolower()] =
94,314✔
1861
                        std::move(otherArgument);
141,471✔
1862
                }
1863
                else
1864
                {
1865
                    CPLError(CE_Failure, CPLE_NotSupported,
×
1866
                             "Unsupported type for parameter %s in "
1867
                             "pszXMLMetadata=%s for %s. Only boolean, string, "
1868
                             "integer, double and double_list are supported",
1869
                             pszName, pszXMLMetadata, pszFuncName);
1870
                    return CE_Failure;
×
1871
                }
1872
            }
1873
        }
1874
    }
1875
    oFunc.eRequestedInputDT = eRequestedInputDT;
7,145✔
1876
    if (nSupportedInputDTSize)
7,145✔
1877
    {
1878
        oFunc.aeSupportedInputDT.insert(
1879
            oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
×
1880
            paeSupportedInputDT + nSupportedInputDTSize);
×
1881
    }
1882
    if (nSupportedInputBandCountSize)
7,145✔
1883
    {
1884
        oFunc.anSupportedInputBandCount.insert(
1885
            oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
×
1886
            panSupportedInputBandCount + nSupportedInputBandCountSize);
×
1887
    }
1888
    oFunc.pfnInit = pfnInit;
7,145✔
1889
    oFunc.pfnFree = pfnFree;
7,145✔
1890
    oFunc.pfnProcess = pfnProcess;
7,145✔
1891

1892
    oMap[pszFuncName] = std::move(oFunc);
7,145✔
1893

1894
    return CE_None;
7,145✔
1895
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc