Coverage Report

Created: 2025-08-28 06:57

/src/gdal/frmts/vrt/vrtprocesseddataset.cpp
Line
Count
Source (jump to first uncovered line)
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()
80
0
{
81
0
    static std::map<std::string, VRTProcessedDatasetFunc> goMap;
82
0
    return goMap;
83
0
}
84
85
/************************************************************************/
86
/*                            Step::~Step()                             */
87
/************************************************************************/
88
89
/*! @cond Doxygen_Suppress */
90
91
/** Step destructor */
92
VRTProcessedDataset::Step::~Step()
93
0
{
94
0
    deinit();
95
0
}
96
97
/************************************************************************/
98
/*                           Step::deinit()                             */
99
/************************************************************************/
100
101
/** Free pWorkingData */
102
void VRTProcessedDataset::Step::deinit()
103
0
{
104
0
    if (pWorkingData)
105
0
    {
106
0
        const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
107
0
        const auto oIterFunc = oMapFunctions.find(osAlgorithm);
108
0
        if (oIterFunc != oMapFunctions.end())
109
0
        {
110
0
            if (oIterFunc->second.pfnFree)
111
0
            {
112
0
                oIterFunc->second.pfnFree(osAlgorithm.c_str(),
113
0
                                          oIterFunc->second.pUserData,
114
0
                                          pWorkingData);
115
0
            }
116
0
        }
117
0
        else
118
0
        {
119
0
            CPLAssert(false);
120
0
        }
121
0
        pWorkingData = nullptr;
122
0
    }
123
0
}
124
125
/************************************************************************/
126
/*                        Step::Step(Step&& other)                      */
127
/************************************************************************/
128
129
/** Move constructor */
130
VRTProcessedDataset::Step::Step(Step &&other)
131
0
    : osAlgorithm(std::move(other.osAlgorithm)),
132
0
      aosArguments(std::move(other.aosArguments)), eInDT(other.eInDT),
133
0
      eOutDT(other.eOutDT), nInBands(other.nInBands),
134
0
      nOutBands(other.nOutBands), adfInNoData(other.adfInNoData),
135
0
      adfOutNoData(other.adfOutNoData), pWorkingData(other.pWorkingData)
136
0
{
137
0
    other.pWorkingData = nullptr;
138
0
}
139
140
/************************************************************************/
141
/*                      Step operator=(Step&& other)                    */
142
/************************************************************************/
143
144
/** Move assignment operator */
145
VRTProcessedDataset::Step &VRTProcessedDataset::Step::operator=(Step &&other)
146
0
{
147
0
    if (&other != this)
148
0
    {
149
0
        deinit();
150
0
        osAlgorithm = std::move(other.osAlgorithm);
151
0
        aosArguments = std::move(other.aosArguments);
152
0
        eInDT = other.eInDT;
153
0
        eOutDT = other.eOutDT;
154
0
        nInBands = other.nInBands;
155
0
        nOutBands = other.nOutBands;
156
0
        adfInNoData = std::move(other.adfInNoData);
157
0
        adfOutNoData = std::move(other.adfOutNoData);
158
0
        std::swap(pWorkingData, other.pWorkingData);
159
0
    }
160
0
    return *this;
161
0
}
162
163
/************************************************************************/
164
/*                        VRTProcessedDataset()                         */
165
/************************************************************************/
166
167
/** Constructor */
168
VRTProcessedDataset::VRTProcessedDataset(int nXSize, int nYSize)
169
0
    : VRTDataset(nXSize, nYSize)
170
0
{
171
0
}
172
173
/************************************************************************/
174
/*                       ~VRTProcessedDataset()                         */
175
/************************************************************************/
176
177
VRTProcessedDataset::~VRTProcessedDataset()
178
179
0
{
180
0
    VRTProcessedDataset::FlushCache(true);
181
0
    VRTProcessedDataset::CloseDependentDatasets();
182
0
}
183
184
/************************************************************************/
185
/*                              XMLInit()                               */
186
/************************************************************************/
187
188
/** Instantiate object from XML tree */
189
CPLErr VRTProcessedDataset::XMLInit(const CPLXMLNode *psTree,
190
                                    const char *pszVRTPathIn)
191
192
0
{
193
0
    if (Init(psTree, pszVRTPathIn, nullptr, nullptr, -1) != CE_None)
194
0
        return CE_Failure;
195
196
0
    const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
197
0
    const int nOvrCount = poSrcFirstBand->GetOverviewCount();
198
0
    for (int i = 0; i < nOvrCount; ++i)
199
0
    {
200
0
        auto poOvrDS = std::make_unique<VRTProcessedDataset>(0, 0);
201
0
        if (poOvrDS->Init(psTree, pszVRTPathIn, this, m_poSrcDS.get(), i) !=
202
0
            CE_None)
203
0
            break;
204
0
        m_apoOverviewDatasets.emplace_back(std::move(poOvrDS));
205
0
    }
206
207
0
    return CE_None;
208
0
}
209
210
static bool HasScaleOffset(GDALDataset &oSrcDS)
211
0
{
212
0
    for (int i = 1; i <= oSrcDS.GetRasterCount(); i++)
213
0
    {
214
0
        int pbSuccess;
215
0
        GDALRasterBand &oBand = *oSrcDS.GetRasterBand(i);
216
0
        double scale = oBand.GetScale(&pbSuccess);
217
0
        if (pbSuccess && scale != 1)
218
0
        {
219
0
            return true;
220
0
        }
221
0
        double offset = oBand.GetOffset(&pbSuccess);
222
0
        if (pbSuccess && offset != 0)
223
0
        {
224
0
            return true;
225
0
        }
226
0
    }
227
228
0
    return false;
229
0
}
230
231
/** Instantiate object from XML tree */
232
CPLErr VRTProcessedDataset::Init(const CPLXMLNode *psTree,
233
                                 const char *pszVRTPathIn,
234
                                 const VRTProcessedDataset *poParentDS,
235
                                 GDALDataset *poParentSrcDS, int iOvrLevel)
236
237
0
{
238
0
    const CPLXMLNode *psInput = CPLGetXMLNode(psTree, "Input");
239
0
    if (!psInput)
240
0
    {
241
0
        CPLError(CE_Failure, CPLE_AppDefined, "Input element missing");
242
0
        return CE_Failure;
243
0
    }
244
245
0
    if (pszVRTPathIn)
246
0
        m_osVRTPath = pszVRTPathIn;
247
248
0
    if (poParentSrcDS)
249
0
    {
250
0
        m_poSrcDS.reset(
251
0
            GDALCreateOverviewDataset(poParentSrcDS, iOvrLevel, true));
252
0
    }
253
0
    else if (const CPLXMLNode *psSourceFileNameNode =
254
0
                 CPLGetXMLNode(psInput, "SourceFilename"))
255
0
    {
256
0
        const bool bRelativeToVRT = CPL_TO_BOOL(
257
0
            atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0")));
258
0
        const std::string osFilename = GDALDataset::BuildFilename(
259
0
            CPLGetXMLValue(psInput, "SourceFilename", ""), pszVRTPathIn,
260
0
            bRelativeToVRT);
261
0
        m_poSrcDS.reset(GDALDataset::Open(
262
0
            osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
263
0
            nullptr, nullptr));
264
0
    }
265
0
    else if (const CPLXMLNode *psVRTDataset =
266
0
                 CPLGetXMLNode(psInput, "VRTDataset"))
267
0
    {
268
0
        CPLXMLNode sVRTDatasetTmp = *psVRTDataset;
269
0
        sVRTDatasetTmp.psNext = nullptr;
270
0
        char *pszXML = CPLSerializeXMLTree(&sVRTDatasetTmp);
271
0
        m_poSrcDS = VRTDataset::OpenXML(pszXML, pszVRTPathIn, GA_ReadOnly);
272
0
        CPLFree(pszXML);
273
0
    }
274
0
    else
275
0
    {
276
0
        CPLError(
277
0
            CE_Failure, CPLE_AppDefined,
278
0
            "Input element should have a SourceFilename or VRTDataset element");
279
0
        return CE_Failure;
280
0
    }
281
282
0
    if (!m_poSrcDS)
283
0
        return CE_Failure;
284
285
0
    const char *pszUnscale = CPLGetXMLValue(psInput, "unscale", "AUTO");
286
0
    bool bUnscale = false;
287
0
    if (EQUAL(pszUnscale, "AUTO"))
288
0
    {
289
0
        if (HasScaleOffset(*m_poSrcDS))
290
0
        {
291
0
            bUnscale = true;
292
0
        }
293
0
    }
294
0
    else if (EQUAL(pszUnscale, "YES") || EQUAL(pszUnscale, "ON") ||
295
0
             EQUAL(pszUnscale, "TRUE") || EQUAL(pszUnscale, "1"))
296
0
    {
297
0
        bUnscale = true;
298
0
    }
299
0
    else if (!(EQUAL(pszUnscale, "NO") || EQUAL(pszUnscale, "OFF") ||
300
0
               EQUAL(pszUnscale, "FALSE") || EQUAL(pszUnscale, "0")))
301
0
    {
302
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid value of 'unscale'");
303
0
        return CE_Failure;
304
0
    }
305
306
0
    if (bUnscale)
307
0
    {
308
0
        CPLStringList oArgs;
309
0
        oArgs.AddString("-unscale");
310
0
        oArgs.AddString("-ot");
311
0
        oArgs.AddString("Float64");
312
0
        oArgs.AddString("-of");
313
0
        oArgs.AddString("VRT");
314
0
        oArgs.AddString("-a_nodata");
315
0
        oArgs.AddString("nan");
316
0
        auto *poArgs = GDALTranslateOptionsNew(oArgs.List(), nullptr);
317
0
        int pbUsageError;
318
0
        CPLAssert(poArgs);
319
0
        m_poVRTSrcDS.reset(m_poSrcDS.release());
320
        // https://trac.cppcheck.net/ticket/11325
321
        // cppcheck-suppress accessMoved
322
0
        m_poSrcDS.reset(GDALDataset::FromHandle(
323
0
            GDALTranslate("", m_poVRTSrcDS.get(), poArgs, &pbUsageError)));
324
0
        GDALTranslateOptionsFree(poArgs);
325
326
0
        if (pbUsageError || !m_poSrcDS)
327
0
        {
328
0
            return CE_Failure;
329
0
        }
330
0
    }
331
332
0
    if (nRasterXSize == 0 && nRasterYSize == 0)
333
0
    {
334
0
        nRasterXSize = m_poSrcDS->GetRasterXSize();
335
0
        nRasterYSize = m_poSrcDS->GetRasterYSize();
336
0
    }
337
0
    else if (nRasterXSize != m_poSrcDS->GetRasterXSize() ||
338
0
             nRasterYSize != m_poSrcDS->GetRasterYSize())
339
0
    {
340
0
        CPLError(CE_Failure, CPLE_AppDefined,
341
0
                 "Inconsistent declared VRT dimensions with input dataset");
342
0
        return CE_Failure;
343
0
    }
344
345
0
    if (m_poSrcDS->GetRasterCount() == 0)
346
0
        return CE_Failure;
347
348
    // Inherit SRS from source if not explicitly defined in VRT
349
0
    if (!CPLGetXMLNode(psTree, "SRS"))
350
0
    {
351
0
        const OGRSpatialReference *poSRS = m_poSrcDS->GetSpatialRef();
352
0
        if (poSRS)
353
0
        {
354
0
            m_poSRS.reset(poSRS->Clone());
355
0
        }
356
0
    }
357
358
    // Inherit GeoTransform from source if not explicitly defined in VRT
359
0
    if (iOvrLevel < 0 && !CPLGetXMLNode(psTree, "GeoTransform"))
360
0
    {
361
0
        if (m_poSrcDS->GetGeoTransform(m_gt) == CE_None)
362
0
            m_bGeoTransformSet = true;
363
0
    }
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
0
    const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
372
0
    poSrcFirstBand->GetBlockSize(&m_nBlockXSize, &m_nBlockYSize);
373
0
    bool bUserBlockSize = false;
374
0
    if (const char *pszBlockXSize =
375
0
            CPLGetXMLValue(psTree, "BlockXSize", nullptr))
376
0
    {
377
0
        bUserBlockSize = true;
378
0
        m_nBlockXSize = atoi(pszBlockXSize);
379
0
        if (m_nBlockXSize <= 1)
380
0
        {
381
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockXSize");
382
0
            return CE_Failure;
383
0
        }
384
0
    }
385
0
    if (const char *pszBlockYSize =
386
0
            CPLGetXMLValue(psTree, "BlockYSize", nullptr))
387
0
    {
388
0
        bUserBlockSize = true;
389
0
        m_nBlockYSize = atoi(pszBlockYSize);
390
0
        if (m_nBlockYSize <= 1)
391
0
        {
392
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockYSize");
393
0
            return CE_Failure;
394
0
        }
395
0
    }
396
397
    // Initialize all the general VRT stuff.
398
0
    if (VRTDataset::XMLInit(psTree, pszVRTPathIn) != CE_None)
399
0
    {
400
0
        return CE_Failure;
401
0
    }
402
403
    // Use geotransform from parent for overviews
404
0
    if (iOvrLevel >= 0 && poParentDS->m_bGeoTransformSet)
405
0
    {
406
0
        m_bGeoTransformSet = true;
407
0
        m_gt = poParentDS->m_gt;
408
0
        m_gt[1] *=
409
0
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
410
0
        m_gt[2] *=
411
0
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
412
0
        m_gt[4] *=
413
0
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
414
0
        m_gt[5] *=
415
0
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
416
0
    }
417
418
0
    const CPLXMLNode *psOutputBands = CPLGetXMLNode(psTree, "OutputBands");
419
0
    if (psOutputBands)
420
0
    {
421
0
        if (const char *pszCount =
422
0
                CPLGetXMLValue(psOutputBands, "count", nullptr))
423
0
        {
424
0
            if (EQUAL(pszCount, "FROM_LAST_STEP"))
425
0
            {
426
0
                m_outputBandCountProvenance = ValueProvenance::FROM_LAST_STEP;
427
0
            }
428
0
            else if (!EQUAL(pszCount, "FROM_SOURCE"))
429
0
            {
430
0
                if (CPLGetValueType(pszCount) == CPL_VALUE_INTEGER)
431
0
                {
432
0
                    m_outputBandCountProvenance =
433
0
                        ValueProvenance::USER_PROVIDED;
434
0
                    m_outputBandCountValue = atoi(pszCount);
435
0
                    if (!GDALCheckBandCount(m_outputBandCountValue,
436
0
                                            /* bIsZeroAllowed = */ false))
437
0
                    {
438
                        // Error emitted by above function
439
0
                        return CE_Failure;
440
0
                    }
441
0
                }
442
0
                else
443
0
                {
444
0
                    CPLError(CE_Failure, CPLE_AppDefined,
445
0
                             "Invalid value for OutputBands.count");
446
0
                    return CE_Failure;
447
0
                }
448
0
            }
449
0
        }
450
451
0
        if (const char *pszType =
452
0
                CPLGetXMLValue(psOutputBands, "dataType", nullptr))
453
0
        {
454
0
            if (EQUAL(pszType, "FROM_LAST_STEP"))
455
0
            {
456
0
                m_outputBandDataTypeProvenance =
457
0
                    ValueProvenance::FROM_LAST_STEP;
458
0
            }
459
0
            else if (!EQUAL(pszType, "FROM_SOURCE"))
460
0
            {
461
0
                m_outputBandDataTypeProvenance = ValueProvenance::USER_PROVIDED;
462
0
                m_outputBandDataTypeValue = GDALGetDataTypeByName(pszType);
463
0
                if (m_outputBandDataTypeValue == GDT_Unknown)
464
0
                {
465
0
                    CPLError(CE_Failure, CPLE_AppDefined,
466
0
                             "Invalid value for OutputBands.dataType");
467
0
                    return CE_Failure;
468
0
                }
469
0
            }
470
0
        }
471
0
    }
472
0
    else if (CPLGetXMLNode(psTree, "VRTRasterBand"))
473
0
    {
474
0
        m_outputBandCountProvenance = ValueProvenance::FROM_VRTRASTERBAND;
475
0
        m_outputBandDataTypeProvenance = ValueProvenance::FROM_VRTRASTERBAND;
476
0
    }
477
478
0
    int nOutputBandCount = 0;
479
0
    switch (m_outputBandCountProvenance)
480
0
    {
481
0
        case ValueProvenance::USER_PROVIDED:
482
0
            nOutputBandCount = m_outputBandCountValue;
483
0
            break;
484
0
        case ValueProvenance::FROM_SOURCE:
485
0
            nOutputBandCount = m_poSrcDS->GetRasterCount();
486
0
            break;
487
0
        case ValueProvenance::FROM_VRTRASTERBAND:
488
0
            nOutputBandCount = nBands;
489
0
            break;
490
0
        case ValueProvenance::FROM_LAST_STEP:
491
0
            break;
492
0
    }
493
494
0
    const CPLXMLNode *psProcessingSteps =
495
0
        CPLGetXMLNode(psTree, "ProcessingSteps");
496
0
    if (!psProcessingSteps)
497
0
    {
498
0
        CPLError(CE_Failure, CPLE_AppDefined,
499
0
                 "ProcessingSteps element missing");
500
0
        return CE_Failure;
501
0
    }
502
503
0
    const auto eInDT = poSrcFirstBand->GetRasterDataType();
504
0
    for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
505
0
    {
506
0
        const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
507
0
        if (eDT != eInDT)
508
0
        {
509
0
            CPLError(CE_Warning, CPLE_AppDefined,
510
0
                     "Not all bands of the input dataset have the same data "
511
0
                     "type. The data type of the first band will be used as "
512
0
                     "the reference one.");
513
0
            break;
514
0
        }
515
0
    }
516
517
0
    GDALDataType eCurrentDT = eInDT;
518
0
    int nCurrentBandCount = m_poSrcDS->GetRasterCount();
519
520
0
    std::vector<double> adfNoData;
521
0
    for (int i = 1; i <= nCurrentBandCount; ++i)
522
0
    {
523
0
        int bHasVal = FALSE;
524
0
        const double dfVal =
525
0
            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
526
0
        adfNoData.emplace_back(
527
0
            bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
528
0
    }
529
530
0
    int nStepCount = 0;
531
0
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
532
0
         psStep = psStep->psNext)
533
0
    {
534
0
        if (psStep->eType == CXT_Element &&
535
0
            strcmp(psStep->pszValue, "Step") == 0)
536
0
        {
537
0
            ++nStepCount;
538
0
        }
539
0
    }
540
541
0
    int iStep = 0;
542
0
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
543
0
         psStep = psStep->psNext)
544
0
    {
545
0
        if (psStep->eType == CXT_Element &&
546
0
            strcmp(psStep->pszValue, "Step") == 0)
547
0
        {
548
0
            ++iStep;
549
0
            const bool bIsFinalStep = (iStep == nStepCount);
550
0
            std::vector<double> adfOutNoData;
551
0
            if (bIsFinalStep)
552
0
            {
553
                // Initialize adfOutNoData with nodata value of *output* bands
554
                // for final step
555
0
                if (m_outputBandCountProvenance ==
556
0
                    ValueProvenance::FROM_VRTRASTERBAND)
557
0
                {
558
0
                    for (int i = 1; i <= nBands; ++i)
559
0
                    {
560
0
                        int bHasVal = FALSE;
561
0
                        const double dfVal =
562
0
                            GetRasterBand(i)->GetNoDataValue(&bHasVal);
563
0
                        adfOutNoData.emplace_back(
564
0
                            bHasVal ? dfVal
565
0
                                    : std::numeric_limits<double>::quiet_NaN());
566
0
                    }
567
0
                }
568
0
                else if (m_outputBandCountProvenance ==
569
0
                         ValueProvenance::FROM_SOURCE)
570
0
                {
571
0
                    for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
572
0
                    {
573
0
                        int bHasVal = FALSE;
574
0
                        const double dfVal =
575
0
                            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(
576
0
                                &bHasVal);
577
0
                        adfOutNoData.emplace_back(
578
0
                            bHasVal ? dfVal
579
0
                                    : std::numeric_limits<double>::quiet_NaN());
580
0
                    }
581
0
                }
582
0
                else if (m_outputBandCountProvenance ==
583
0
                         ValueProvenance::USER_PROVIDED)
584
0
                {
585
0
                    adfOutNoData.resize(
586
0
                        m_outputBandCountValue,
587
0
                        std::numeric_limits<double>::quiet_NaN());
588
0
                }
589
0
            }
590
0
            if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
591
0
                           adfNoData, adfOutNoData))
592
0
                return CE_Failure;
593
0
            adfNoData = std::move(adfOutNoData);
594
0
        }
595
0
    }
596
597
0
    if (m_aoSteps.empty())
598
0
    {
599
0
        CPLError(CE_Failure, CPLE_AppDefined,
600
0
                 "At least one step should be defined");
601
0
        return CE_Failure;
602
0
    }
603
604
0
    int nLargestInDTSizeTimesBand = 1;
605
0
    int nLargestOutDTSizeTimesBand = 1;
606
0
    for (const auto &oStep : m_aoSteps)
607
0
    {
608
0
        const int nInDTSizeTimesBand =
609
0
            GDALGetDataTypeSizeBytes(oStep.eInDT) * oStep.nInBands;
610
0
        nLargestInDTSizeTimesBand =
611
0
            std::max(nLargestInDTSizeTimesBand, nInDTSizeTimesBand);
612
0
        const int nOutDTSizeTimesBand =
613
0
            GDALGetDataTypeSizeBytes(oStep.eOutDT) * oStep.nOutBands;
614
0
        nLargestOutDTSizeTimesBand =
615
0
            std::max(nLargestOutDTSizeTimesBand, nOutDTSizeTimesBand);
616
0
    }
617
0
    m_nWorkingBytesPerPixel =
618
0
        nLargestInDTSizeTimesBand + nLargestOutDTSizeTimesBand;
619
620
    // Use only up to 40% of RAM to acquire source bands and generate the output
621
    // buffer.
622
0
    m_nAllowedRAMUsage = CPLGetUsablePhysicalRAM() / 10 * 4;
623
    // Only for tests now
624
0
    const char *pszMAX_RAM = "VRT_PROCESSED_DATASET_ALLOWED_RAM_USAGE";
625
0
    if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
626
0
    {
627
0
        CPL_IGNORE_RET_VAL(
628
0
            CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
629
0
    }
630
631
0
    if (m_nAllowedRAMUsage > 0)
632
0
    {
633
0
        bool bBlockSizeModified = false;
634
0
        while ((m_nBlockXSize >= 2 || m_nBlockYSize >= 2) &&
635
0
               static_cast<GIntBig>(m_nBlockXSize) * m_nBlockYSize >
636
0
                   m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
637
0
        {
638
0
            if ((m_nBlockXSize == nRasterXSize ||
639
0
                 m_nBlockYSize >= m_nBlockXSize) &&
640
0
                m_nBlockYSize >= 2)
641
0
            {
642
0
                m_nBlockYSize /= 2;
643
0
            }
644
0
            else
645
0
            {
646
0
                m_nBlockXSize /= 2;
647
0
            }
648
0
            bBlockSizeModified = true;
649
0
        }
650
0
        if (bBlockSizeModified)
651
0
        {
652
0
            if (bUserBlockSize)
653
0
            {
654
0
                CPLError(
655
0
                    CE_Warning, CPLE_AppDefined,
656
0
                    "Reducing block size to %d x %d to avoid consuming too "
657
0
                    "much RAM",
658
0
                    m_nBlockXSize, m_nBlockYSize);
659
0
            }
660
0
            else
661
0
            {
662
0
                CPLDebug(
663
0
                    "VRT",
664
0
                    "Reducing block size to %d x %d to avoid consuming too "
665
0
                    "much RAM",
666
0
                    m_nBlockXSize, m_nBlockYSize);
667
0
            }
668
0
        }
669
0
    }
670
671
0
    if (m_outputBandCountProvenance == ValueProvenance::FROM_LAST_STEP)
672
0
    {
673
0
        nOutputBandCount = nCurrentBandCount;
674
0
    }
675
0
    else if (nOutputBandCount != nCurrentBandCount)
676
0
    {
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
0
        CPLError(
681
0
            CE_Failure, CPLE_AppDefined,
682
0
            "Number of output bands of last step (%d) is not consistent with "
683
0
            "number of VRTProcessedRasterBand's (%d)",
684
0
            nCurrentBandCount, nBands);
685
0
        return CE_Failure;
686
0
    }
687
688
0
    if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
689
0
    {
690
0
        m_outputBandDataTypeValue = eCurrentDT;
691
0
    }
692
693
0
    const auto ClearBands = [this]()
694
0
    {
695
0
        for (int i = 0; i < nBands; ++i)
696
0
            delete papoBands[i];
697
0
        CPLFree(papoBands);
698
0
        papoBands = nullptr;
699
0
        nBands = 0;
700
0
    };
701
702
0
    if (nBands != 0 &&
703
0
        (nBands != nOutputBandCount ||
704
0
         (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP &&
705
0
          m_outputBandDataTypeValue != papoBands[0]->GetRasterDataType())))
706
0
    {
707
0
        ClearBands();
708
0
    }
709
710
0
    const auto GetOutputBandType = [this, eCurrentDT](GDALDataType eSourceDT)
711
0
    {
712
0
        if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
713
0
            return eCurrentDT;
714
0
        else if (m_outputBandDataTypeProvenance ==
715
0
                 ValueProvenance::USER_PROVIDED)
716
0
            return m_outputBandDataTypeValue;
717
0
        else
718
0
            return eSourceDT;
719
0
    };
720
721
0
    if (m_outputBandCountProvenance == ValueProvenance::FROM_SOURCE)
722
0
    {
723
0
        for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
724
0
        {
725
0
            const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
726
0
            const GDALDataType eOutputBandType =
727
0
                GetOutputBandType(poSrcBand->GetRasterDataType());
728
0
            auto poBand =
729
0
                new VRTProcessedRasterBand(this, i + 1, eOutputBandType);
730
0
            poBand->CopyCommonInfoFrom(poSrcBand);
731
0
            SetBand(i + 1, poBand);
732
0
        }
733
0
    }
734
0
    else if (m_outputBandCountProvenance != ValueProvenance::FROM_VRTRASTERBAND)
735
0
    {
736
0
        const GDALDataType eOutputBandType = GetOutputBandType(eInDT);
737
738
0
        bool bClearAndSetBands = true;
739
0
        if (nBands == nOutputBandCount)
740
0
        {
741
0
            bClearAndSetBands = false;
742
0
            for (int i = 0; i < nBands; ++i)
743
0
            {
744
0
                bClearAndSetBands =
745
0
                    bClearAndSetBands ||
746
0
                    !dynamic_cast<VRTProcessedRasterBand *>(papoBands[i]) ||
747
0
                    papoBands[i]->GetRasterDataType() != eOutputBandType;
748
0
            }
749
0
        }
750
0
        if (bClearAndSetBands)
751
0
        {
752
0
            ClearBands();
753
0
            for (int i = 0; i < nOutputBandCount; ++i)
754
0
            {
755
0
                SetBand(i + 1, std::make_unique<VRTProcessedRasterBand>(
756
0
                                   this, i + 1, eOutputBandType));
757
0
            }
758
0
        }
759
0
    }
760
761
0
    if (nBands > 1)
762
0
        SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
763
764
0
    m_oXMLTree.reset(CPLCloneXMLTree(psTree));
765
766
0
    return CE_None;
767
0
}
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,
789
                                    GDALDataType &eCurrentDT,
790
                                    int &nCurrentBandCount,
791
                                    std::vector<double> &adfInNoData,
792
                                    std::vector<double> &adfOutNoData)
793
0
{
794
0
    const char *pszStepName = CPLGetXMLValue(
795
0
        psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
796
0
    const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
797
0
    if (!pszAlgorithm)
798
0
    {
799
0
        CPLError(CE_Failure, CPLE_AppDefined,
800
0
                 "Step '%s' lacks a Algorithm element", pszStepName);
801
0
        return false;
802
0
    }
803
804
0
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
805
0
    const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
806
0
    if (oIterFunc == oMapFunctions.end())
807
0
    {
808
0
        CPLError(CE_Failure, CPLE_AppDefined,
809
0
                 "Step '%s' uses unregistered algorithm '%s'", pszStepName,
810
0
                 pszAlgorithm);
811
0
        return false;
812
0
    }
813
814
0
    const auto &oFunc = oIterFunc->second;
815
816
0
    if (!oFunc.aeSupportedInputDT.empty())
817
0
    {
818
0
        if (std::find(oFunc.aeSupportedInputDT.begin(),
819
0
                      oFunc.aeSupportedInputDT.end(),
820
0
                      eCurrentDT) == oFunc.aeSupportedInputDT.end())
821
0
        {
822
0
            CPLError(CE_Failure, CPLE_AppDefined,
823
0
                     "Step '%s' (using algorithm '%s') does not "
824
0
                     "support input data type = '%s'",
825
0
                     pszStepName, pszAlgorithm,
826
0
                     GDALGetDataTypeName(eCurrentDT));
827
0
            return false;
828
0
        }
829
0
    }
830
831
0
    if (!oFunc.anSupportedInputBandCount.empty())
832
0
    {
833
0
        if (std::find(oFunc.anSupportedInputBandCount.begin(),
834
0
                      oFunc.anSupportedInputBandCount.end(),
835
0
                      nCurrentBandCount) ==
836
0
            oFunc.anSupportedInputBandCount.end())
837
0
        {
838
0
            CPLError(CE_Failure, CPLE_AppDefined,
839
0
                     "Step '%s' (using algorithm '%s') does not "
840
0
                     "support input band count = %d",
841
0
                     pszStepName, pszAlgorithm, nCurrentBandCount);
842
0
            return false;
843
0
        }
844
0
    }
845
846
0
    Step oStep;
847
0
    oStep.osAlgorithm = pszAlgorithm;
848
0
    oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
849
0
                      ? oFunc.eRequestedInputDT
850
0
                      : eCurrentDT;
851
0
    oStep.nInBands = nCurrentBandCount;
852
853
    // Unless modified by pfnInit...
854
0
    oStep.eOutDT = oStep.eInDT;
855
856
0
    oStep.adfInNoData = adfInNoData;
857
0
    oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
858
859
    // Deal with constant arguments
860
0
    for (const auto &nameValuePair : oFunc.oMapConstantArguments)
861
0
    {
862
0
        oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
863
0
                                        nameValuePair.second.c_str());
864
0
    }
865
866
    // Deal with built-in arguments
867
0
    if (oFunc.oSetBuiltinArguments.find("nodata") !=
868
0
        oFunc.oSetBuiltinArguments.end())
869
0
    {
870
0
        int bHasVal = false;
871
0
        const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
872
0
        const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
873
0
        if (bHasVal)
874
0
        {
875
0
            oStep.aosArguments.AddNameValue("nodata",
876
0
                                            CPLSPrintf("%.17g", dfVal));
877
0
        }
878
0
    }
879
880
0
    if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
881
0
        oFunc.oSetBuiltinArguments.end())
882
0
    {
883
0
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
884
0
        {
885
0
            int bHasVal = false;
886
0
            const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
887
0
            oStep.aosArguments.AddNameValue(
888
0
                CPLSPrintf("offset_%d", i),
889
0
                CPLSPrintf("%.17g", bHasVal ? dfVal : 0.0));
890
0
        }
891
0
    }
892
893
0
    if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
894
0
        oFunc.oSetBuiltinArguments.end())
895
0
    {
896
0
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
897
0
        {
898
0
            int bHasVal = false;
899
0
            const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
900
0
            oStep.aosArguments.AddNameValue(
901
0
                CPLSPrintf("scale_%d", i),
902
0
                CPLSPrintf("%.17g", bHasVal ? dfVal : 1.0));
903
0
        }
904
0
    }
905
906
    // Parse arguments specified in VRT
907
0
    std::set<std::string> oFoundArguments;
908
909
0
    for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
910
0
         psStepChild = psStepChild->psNext)
911
0
    {
912
0
        if (psStepChild->eType == CXT_Element &&
913
0
            strcmp(psStepChild->pszValue, "Argument") == 0)
914
0
        {
915
0
            const char *pszParamName =
916
0
                CPLGetXMLValue(psStepChild, "name", nullptr);
917
0
            if (!pszParamName)
918
0
            {
919
0
                CPLError(CE_Failure, CPLE_AppDefined,
920
0
                         "Step '%s' has a Argument without a name attribute",
921
0
                         pszStepName);
922
0
                return false;
923
0
            }
924
0
            const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
925
0
            auto oOtherArgIter =
926
0
                oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
927
0
            if (!oFunc.oOtherArguments.empty() &&
928
0
                oOtherArgIter == oFunc.oOtherArguments.end())
929
0
            {
930
                // If we got a parameter name like 'coefficients_1',
931
                // try to fetch the generic 'coefficients_{band}'
932
0
                std::string osParamName(pszParamName);
933
0
                const auto nPos = osParamName.rfind('_');
934
0
                if (nPos != std::string::npos)
935
0
                {
936
0
                    osParamName.resize(nPos + 1);
937
0
                    osParamName += "{band}";
938
0
                    oOtherArgIter = oFunc.oOtherArguments.find(
939
0
                        CPLString(osParamName).tolower());
940
0
                }
941
0
            }
942
0
            if (oOtherArgIter != oFunc.oOtherArguments.end())
943
0
            {
944
0
                oFoundArguments.insert(oOtherArgIter->first);
945
946
0
                const std::string &osType = oOtherArgIter->second.osType;
947
0
                if (osType == "boolean")
948
0
                {
949
0
                    if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
950
0
                    {
951
0
                        CPLError(CE_Failure, CPLE_NotSupported,
952
0
                                 "Step '%s' has a Argument '%s' whose "
953
0
                                 "value '%s' is not a boolean",
954
0
                                 pszStepName, pszParamName, pszValue);
955
0
                        return false;
956
0
                    }
957
0
                }
958
0
                else if (osType == "integer")
959
0
                {
960
0
                    if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
961
0
                    {
962
0
                        CPLError(CE_Failure, CPLE_NotSupported,
963
0
                                 "Step '%s' has a Argument '%s' whose "
964
0
                                 "value '%s' is not a integer",
965
0
                                 pszStepName, pszParamName, pszValue);
966
0
                        return false;
967
0
                    }
968
0
                }
969
0
                else if (osType == "double")
970
0
                {
971
0
                    const auto eType = CPLGetValueType(pszValue);
972
0
                    if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
973
0
                    {
974
0
                        CPLError(CE_Failure, CPLE_NotSupported,
975
0
                                 "Step '%s' has a Argument '%s' whose "
976
0
                                 "value '%s' is not a double",
977
0
                                 pszStepName, pszParamName, pszValue);
978
0
                        return false;
979
0
                    }
980
0
                }
981
0
                else if (osType == "double_list")
982
0
                {
983
0
                    const CPLStringList aosTokens(
984
0
                        CSLTokenizeString2(pszValue, ",", 0));
985
0
                    for (int i = 0; i < aosTokens.size(); ++i)
986
0
                    {
987
0
                        const auto eType = CPLGetValueType(aosTokens[i]);
988
0
                        if (eType != CPL_VALUE_INTEGER &&
989
0
                            eType != CPL_VALUE_REAL)
990
0
                        {
991
0
                            CPLError(CE_Failure, CPLE_NotSupported,
992
0
                                     "Step '%s' has a Argument '%s' "
993
0
                                     "whose value '%s' is not a "
994
0
                                     "comma-separated list of doubles",
995
0
                                     pszStepName, pszParamName, pszValue);
996
0
                            return false;
997
0
                        }
998
0
                    }
999
0
                }
1000
0
                else if (osType != "string")
1001
0
                {
1002
0
                    CPLDebug("VRT", "Unhandled argument type '%s'",
1003
0
                             osType.c_str());
1004
0
                    CPLAssert(0);
1005
0
                }
1006
0
            }
1007
0
            else if (oFunc.bMetadataSpecified &&
1008
0
                     oFunc.oSetBuiltinArguments.find(
1009
0
                         CPLString(pszParamName).tolower()) ==
1010
0
                         oFunc.oSetBuiltinArguments.end() &&
1011
0
                     oFunc.oMapConstantArguments.find(
1012
0
                         CPLString(pszParamName).tolower()) ==
1013
0
                         oFunc.oMapConstantArguments.end())
1014
0
            {
1015
0
                CPLError(CE_Warning, CPLE_NotSupported,
1016
0
                         "Step '%s' has a Argument '%s' which is not "
1017
0
                         "supported",
1018
0
                         pszStepName, pszParamName);
1019
0
            }
1020
1021
0
            oStep.aosArguments.AddNameValue(pszParamName, pszValue);
1022
0
        }
1023
0
    }
1024
1025
    // Check that required arguments have been specified
1026
0
    for (const auto &oIter : oFunc.oOtherArguments)
1027
0
    {
1028
0
        if (oIter.second.bRequired &&
1029
0
            oFoundArguments.find(oIter.first) == oFoundArguments.end())
1030
0
        {
1031
0
            CPLError(CE_Failure, CPLE_AppDefined,
1032
0
                     "Step '%s' lacks required Argument '%s'", pszStepName,
1033
0
                     oIter.first.c_str());
1034
0
            return false;
1035
0
        }
1036
0
    }
1037
1038
0
    if (oFunc.pfnInit)
1039
0
    {
1040
0
        double *padfOutNoData = nullptr;
1041
0
        if (bIsFinalStep && !adfOutNoData.empty())
1042
0
        {
1043
0
            oStep.nOutBands = static_cast<int>(adfOutNoData.size());
1044
0
            padfOutNoData = static_cast<double *>(
1045
0
                CPLMalloc(adfOutNoData.size() * sizeof(double)));
1046
0
            memcpy(padfOutNoData, adfOutNoData.data(),
1047
0
                   adfOutNoData.size() * sizeof(double));
1048
0
        }
1049
0
        else
1050
0
        {
1051
0
            oStep.nOutBands = 0;
1052
0
        }
1053
1054
0
        if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
1055
0
                          oStep.aosArguments.List(), oStep.nInBands,
1056
0
                          oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
1057
0
                          &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
1058
0
                          &(oStep.pWorkingData)) != CE_None)
1059
0
        {
1060
0
            CPLError(CE_Failure, CPLE_AppDefined,
1061
0
                     "Step '%s' (using algorithm '%s') init() function "
1062
0
                     "failed",
1063
0
                     pszStepName, pszAlgorithm);
1064
0
            CPLFree(padfOutNoData);
1065
0
            return false;
1066
0
        }
1067
1068
        // Input nodata values may have been modified by pfnInit()
1069
0
        oStep.adfInNoData = adfInNoData;
1070
1071
0
        if (padfOutNoData)
1072
0
        {
1073
0
            adfOutNoData = std::vector<double>(padfOutNoData,
1074
0
                                               padfOutNoData + oStep.nOutBands);
1075
0
        }
1076
0
        else
1077
0
        {
1078
0
            adfOutNoData = std::vector<double>(
1079
0
                oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
1080
0
        }
1081
0
        CPLFree(padfOutNoData);
1082
1083
0
        oStep.adfOutNoData = adfOutNoData;
1084
0
    }
1085
0
    else
1086
0
    {
1087
0
        oStep.nOutBands = oStep.nInBands;
1088
0
        adfOutNoData = oStep.adfOutNoData;
1089
0
    }
1090
1091
0
    eCurrentDT = oStep.eOutDT;
1092
0
    nCurrentBandCount = oStep.nOutBands;
1093
1094
0
    m_aoSteps.emplace_back(std::move(oStep));
1095
1096
0
    return true;
1097
0
}
1098
1099
/************************************************************************/
1100
/*                           SerializeToXML()                           */
1101
/************************************************************************/
1102
1103
CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
1104
1105
0
{
1106
0
    CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
1107
0
    if (psTree == nullptr)
1108
0
        return psTree;
1109
1110
    /* -------------------------------------------------------------------- */
1111
    /*      Remove VRTRasterBand nodes from the original tree and find the  */
1112
    /*      last child.                                                     */
1113
    /* -------------------------------------------------------------------- */
1114
0
    CPLXMLNode *psLastChild = psTree->psChild;
1115
0
    CPLXMLNode *psPrevChild = nullptr;
1116
0
    while (psLastChild)
1117
0
    {
1118
0
        CPLXMLNode *psNextChild = psLastChild->psNext;
1119
0
        if (psLastChild->eType == CXT_Element &&
1120
0
            strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
1121
0
        {
1122
0
            if (psPrevChild)
1123
0
                psPrevChild->psNext = psNextChild;
1124
0
            else
1125
0
                psTree->psChild = psNextChild;
1126
0
            psLastChild->psNext = nullptr;
1127
0
            CPLDestroyXMLNode(psLastChild);
1128
0
            psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
1129
0
        }
1130
0
        else if (!psNextChild)
1131
0
        {
1132
0
            break;
1133
0
        }
1134
0
        else
1135
0
        {
1136
0
            psPrevChild = psLastChild;
1137
0
            psLastChild = psNextChild;
1138
0
        }
1139
0
    }
1140
0
    CPLAssert(psLastChild);  // we have at least Input
1141
1142
    /* -------------------------------------------------------------------- */
1143
    /*      Serialize bands.                                                */
1144
    /* -------------------------------------------------------------------- */
1145
0
    bool bHasWarnedAboutRAMUsage = false;
1146
0
    size_t nAccRAMUsage = 0;
1147
0
    for (int iBand = 0; iBand < nBands; iBand++)
1148
0
    {
1149
0
        CPLXMLNode *psBandTree =
1150
0
            static_cast<VRTRasterBand *>(papoBands[iBand])
1151
0
                ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
1152
0
                                 nAccRAMUsage);
1153
1154
0
        if (psBandTree != nullptr)
1155
0
        {
1156
0
            psLastChild->psNext = psBandTree;
1157
0
            psLastChild = psBandTree;
1158
0
        }
1159
0
    }
1160
1161
0
    return psTree;
1162
0
}
1163
1164
/************************************************************************/
1165
/*                           SerializeToXML()                           */
1166
/************************************************************************/
1167
1168
CPLXMLNode *
1169
VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
1170
                                       bool &bHasWarnedAboutRAMUsage,
1171
                                       size_t &nAccRAMUsage)
1172
1173
0
{
1174
0
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1175
0
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1176
1177
    /* -------------------------------------------------------------------- */
1178
    /*      Set subclass.                                                   */
1179
    /* -------------------------------------------------------------------- */
1180
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1181
0
                     CXT_Text, "VRTProcessedRasterBand");
1182
1183
0
    return psTree;
1184
0
}
1185
1186
/************************************************************************/
1187
/*                            GetBlockSize()                            */
1188
/************************************************************************/
1189
1190
/** Return block size */
1191
void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
1192
                                       int *pnBlockYSize) const
1193
1194
0
{
1195
0
    *pnBlockXSize = m_nBlockXSize;
1196
0
    *pnBlockYSize = m_nBlockYSize;
1197
0
}
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,
1208
                                        int nBufYSize,
1209
                                        GDALProgressFunc pfnProgress,
1210
                                        void *pProgressData)
1211
0
{
1212
1213
0
    CPLAssert(!m_aoSteps.empty());
1214
1215
0
    const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
1216
1217
0
    const int nFirstBandCount = m_aoSteps.front().nInBands;
1218
0
    CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
1219
0
    const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
1220
0
    const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
1221
0
    auto &abyInput = m_abyInput;
1222
0
    auto &abyOutput = m_abyOutput;
1223
1224
0
    const char *pszInterleave =
1225
0
        m_poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
1226
0
    if (nFirstBandCount > 1 && (!pszInterleave || EQUAL(pszInterleave, "BAND")))
1227
0
    {
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
0
        const auto eSrcDT = m_poSrcDS->GetRasterBand(1)->GetRasterDataType();
1235
0
        try
1236
0
        {
1237
0
            abyInput.resize(nPixelCount * nFirstBandCount *
1238
0
                            GDALGetDataTypeSizeBytes(eSrcDT));
1239
0
        }
1240
0
        catch (const std::bad_alloc &)
1241
0
        {
1242
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1243
0
                     "Out of memory allocating working buffer");
1244
0
            return false;
1245
0
        }
1246
1247
0
        try
1248
0
        {
1249
0
            abyOutput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1250
0
        }
1251
0
        catch (const std::bad_alloc &)
1252
0
        {
1253
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1254
0
                     "Out of memory allocating working buffer");
1255
0
            return false;
1256
0
        }
1257
1258
0
        GDALRasterIOExtraArg sArg;
1259
0
        INIT_RASTERIO_EXTRA_ARG(sArg);
1260
0
        sArg.pfnProgress = GDALScaledProgress;
1261
0
        sArg.pProgressData =
1262
0
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1263
0
        if (sArg.pProgressData == nullptr)
1264
0
            sArg.pfnProgress = nullptr;
1265
1266
0
        CPLDebugOnly("VRT", "ProcessRegion(): start RasterIO()");
1267
0
        const bool bOK =
1268
0
            m_poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nBufXSize, nBufYSize,
1269
0
                                abyInput.data(), nBufXSize, nBufYSize, eSrcDT,
1270
0
                                nFirstBandCount, nullptr, 0, 0, 0,
1271
0
                                &sArg) == CE_None;
1272
0
        CPLDebugOnly("VRT", "ProcessRegion(): end RasterIO()");
1273
0
        GDALDestroyScaledProgress(sArg.pProgressData);
1274
0
        if (!bOK)
1275
0
            return false;
1276
1277
0
        CPLDebugOnly("VRT", "ProcessRegion(): start GDALTranspose2D()");
1278
0
        GDALTranspose2D(abyInput.data(), eSrcDT, abyOutput.data(), eFirstDT,
1279
0
                        static_cast<size_t>(nBufXSize) * nBufYSize,
1280
0
                        nFirstBandCount);
1281
0
        CPLDebugOnly("VRT", "ProcessRegion(): end GDALTranspose2D()");
1282
1283
        // Swap arrays
1284
0
        std::swap(abyInput, abyOutput);
1285
0
    }
1286
0
    else
1287
0
    {
1288
0
        try
1289
0
        {
1290
0
            abyInput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1291
0
        }
1292
0
        catch (const std::bad_alloc &)
1293
0
        {
1294
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1295
0
                     "Out of memory allocating working buffer");
1296
0
            return false;
1297
0
        }
1298
1299
0
        GDALRasterIOExtraArg sArg;
1300
0
        INIT_RASTERIO_EXTRA_ARG(sArg);
1301
0
        sArg.pfnProgress = GDALScaledProgress;
1302
0
        sArg.pProgressData =
1303
0
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1304
0
        if (sArg.pProgressData == nullptr)
1305
0
            sArg.pfnProgress = nullptr;
1306
1307
0
        const bool bOK =
1308
0
            m_poSrcDS->RasterIO(
1309
0
                GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
1310
0
                nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
1311
0
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
1312
0
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount *
1313
0
                    nBufXSize,
1314
0
                nFirstDTSize, &sArg) == CE_None;
1315
1316
0
        GDALDestroyScaledProgress(sArg.pProgressData);
1317
0
        if (!bOK)
1318
0
            return false;
1319
0
    }
1320
1321
0
    const double dfSrcXOff = nXOff;
1322
0
    const double dfSrcYOff = nYOff;
1323
0
    const double dfSrcXSize = nBufXSize;
1324
0
    const double dfSrcYSize = nBufYSize;
1325
1326
0
    GDALGeoTransform srcGT;
1327
0
    if (m_poSrcDS->GetGeoTransform(srcGT) != CE_None)
1328
0
    {
1329
0
        srcGT = GDALGeoTransform();
1330
0
    }
1331
1332
0
    GDALDataType eLastDT = eFirstDT;
1333
0
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
1334
1335
0
    int iStep = 0;
1336
0
    for (const auto &oStep : m_aoSteps)
1337
0
    {
1338
0
        const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
1339
0
        CPLAssert(oIterFunc != oMapFunctions.end());
1340
1341
        // Data type adaptation
1342
0
        if (eLastDT != oStep.eInDT)
1343
0
        {
1344
0
            try
1345
0
            {
1346
0
                abyOutput.resize(nPixelCount * oStep.nInBands *
1347
0
                                 GDALGetDataTypeSizeBytes(oStep.eInDT));
1348
0
            }
1349
0
            catch (const std::bad_alloc &)
1350
0
            {
1351
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
1352
0
                         "Out of memory allocating working buffer");
1353
0
                return false;
1354
0
            }
1355
1356
0
            GDALCopyWords64(abyInput.data(), eLastDT,
1357
0
                            GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
1358
0
                            oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
1359
0
                            nPixelCount * oStep.nInBands);
1360
1361
0
            std::swap(abyInput, abyOutput);
1362
0
        }
1363
1364
0
        try
1365
0
        {
1366
0
            abyOutput.resize(nPixelCount * oStep.nOutBands *
1367
0
                             GDALGetDataTypeSizeBytes(oStep.eOutDT));
1368
0
        }
1369
0
        catch (const std::bad_alloc &)
1370
0
        {
1371
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1372
0
                     "Out of memory allocating working buffer");
1373
0
            return false;
1374
0
        }
1375
1376
0
        const auto &oFunc = oIterFunc->second;
1377
0
        if (oFunc.pfnProcess(
1378
0
                oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
1379
0
                oStep.aosArguments.List(), nBufXSize, nBufYSize,
1380
0
                abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
1381
0
                oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
1382
0
                oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
1383
0
                dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, srcGT.data(),
1384
0
                m_osVRTPath.c_str(),
1385
0
                /*papszExtra=*/nullptr) != CE_None)
1386
0
        {
1387
0
            return false;
1388
0
        }
1389
1390
0
        std::swap(abyInput, abyOutput);
1391
0
        eLastDT = oStep.eOutDT;
1392
1393
0
        ++iStep;
1394
0
        if (pfnProgress &&
1395
0
            !pfnProgress(0.5 + 0.5 * iStep / static_cast<int>(m_aoSteps.size()),
1396
0
                         "", pProgressData))
1397
0
            return false;
1398
0
    }
1399
1400
0
    return true;
1401
0
}
1402
1403
/************************************************************************/
1404
/*                        VRTProcessedRasterBand()                      */
1405
/************************************************************************/
1406
1407
/** Constructor */
1408
VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
1409
                                               int nBandIn,
1410
                                               GDALDataType eDataTypeIn)
1411
0
{
1412
0
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
1413
1414
0
    poDS = poDSIn;
1415
0
    nBand = nBandIn;
1416
0
    eAccess = GA_Update;
1417
0
    eDataType = eDataTypeIn;
1418
1419
0
    poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
1420
0
}
1421
1422
/************************************************************************/
1423
/*                           GetOverviewCount()                         */
1424
/************************************************************************/
1425
1426
int VRTProcessedRasterBand::GetOverviewCount()
1427
0
{
1428
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1429
0
    return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
1430
0
}
1431
1432
/************************************************************************/
1433
/*                              GetOverview()                           */
1434
/************************************************************************/
1435
1436
GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
1437
0
{
1438
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1439
0
    if (iOvr < 0 ||
1440
0
        iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
1441
0
        return nullptr;
1442
0
    return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
1443
0
}
1444
1445
/************************************************************************/
1446
/*                             IReadBlock()                             */
1447
/************************************************************************/
1448
1449
CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1450
                                          void *pImage)
1451
1452
0
{
1453
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1454
1455
0
    int nBufXSize = 0;
1456
0
    int nBufYSize = 0;
1457
0
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
1458
1459
0
    const int nXPixelOff = nBlockXOff * nBlockXSize;
1460
0
    const int nYPixelOff = nBlockYOff * nBlockYSize;
1461
0
    if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize,
1462
0
                                nullptr, nullptr))
1463
0
    {
1464
0
        return CE_Failure;
1465
0
    }
1466
1467
0
    const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
1468
0
    CPLAssert(nOutBands == poVRTDS->GetRasterCount());
1469
0
    const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
1470
0
    const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1471
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1472
1473
    // Dispatch final output buffer to cached blocks of output bands
1474
0
    for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
1475
0
    {
1476
0
        GDALRasterBlock *poBlock = nullptr;
1477
0
        GByte *pDst;
1478
0
        if (iDstBand + 1 == nBand)
1479
0
        {
1480
0
            pDst = static_cast<GByte *>(pImage);
1481
0
        }
1482
0
        else
1483
0
        {
1484
0
            auto poOtherBand = poVRTDS->papoBands[iDstBand];
1485
0
            poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1486
0
            if (poBlock)
1487
0
            {
1488
0
                poBlock->DropLock();
1489
0
                continue;
1490
0
            }
1491
0
            poBlock = poOtherBand->GetLockedBlockRef(
1492
0
                nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
1493
0
            if (!poBlock)
1494
0
                continue;
1495
0
            pDst = static_cast<GByte *>(poBlock->GetDataRef());
1496
0
        }
1497
0
        for (int iY = 0; iY < nBufYSize; ++iY)
1498
0
        {
1499
0
            GDALCopyWords64(poVRTDS->m_abyInput.data() +
1500
0
                                (iDstBand + static_cast<size_t>(iY) *
1501
0
                                                nBufXSize * nOutBands) *
1502
0
                                    nLastDTSize,
1503
0
                            eLastDT, nLastDTSize * nOutBands,
1504
0
                            pDst +
1505
0
                                static_cast<size_t>(iY) * nBlockXSize * nDTSize,
1506
0
                            eDataType, nDTSize, nBufXSize);
1507
0
        }
1508
0
        if (poBlock)
1509
0
            poBlock->DropLock();
1510
0
    }
1511
1512
0
    return CE_None;
1513
0
}
1514
1515
/************************************************************************/
1516
/*                VRTProcessedDataset::IRasterIO()                      */
1517
/************************************************************************/
1518
1519
CPLErr VRTProcessedDataset::IRasterIO(
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
0
{
1525
    // Try to pass the request to the most appropriate overview dataset.
1526
0
    if (nBufXSize < nXSize && nBufYSize < nYSize)
1527
0
    {
1528
0
        int bTried = FALSE;
1529
0
        const CPLErr eErr = TryOverviewRasterIO(
1530
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1531
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1532
0
            nBandSpace, psExtraArg, &bTried);
1533
0
        if (bTried)
1534
0
            return eErr;
1535
0
    }
1536
1537
    // Optimize reading of all bands at nominal resolution for BIP-like or
1538
    // BSQ-like buffer spacing.
1539
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
1540
0
        nBandCount == nBands)
1541
0
    {
1542
0
        const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1543
0
        const bool bIsBIPLike = nBandSpace == nBufTypeSize &&
1544
0
                                nPixelSpace == nBandSpace * nBands &&
1545
0
                                nLineSpace >= nPixelSpace * nBufXSize &&
1546
0
                                IsAllBands(nBandCount, panBandMap);
1547
0
        const bool bIsBSQLike = nPixelSpace == nBufTypeSize &&
1548
0
                                nLineSpace >= nPixelSpace * nBufXSize &&
1549
0
                                nBandSpace >= nLineSpace * nBufYSize &&
1550
0
                                IsAllBands(nBandCount, panBandMap);
1551
0
        if (bIsBIPLike || bIsBSQLike)
1552
0
        {
1553
0
            GByte *pabyData = static_cast<GByte *>(pData);
1554
            // If acquiring the region of interest in a single time is going
1555
            // to consume too much RAM, split in halves.
1556
0
            if (m_nAllowedRAMUsage > 0 &&
1557
0
                static_cast<GIntBig>(nBufXSize) * nBufYSize >
1558
0
                    m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
1559
0
            {
1560
0
                if ((nBufXSize == nRasterXSize || nBufYSize >= nBufXSize) &&
1561
0
                    nBufYSize >= 2)
1562
0
                {
1563
0
                    GDALRasterIOExtraArg sArg;
1564
0
                    INIT_RASTERIO_EXTRA_ARG(sArg);
1565
0
                    const int nHalfHeight = nBufYSize / 2;
1566
1567
0
                    sArg.pfnProgress = GDALScaledProgress;
1568
0
                    sArg.pProgressData = GDALCreateScaledProgress(
1569
0
                        0, 0.5, psExtraArg->pfnProgress,
1570
0
                        psExtraArg->pProgressData);
1571
0
                    if (sArg.pProgressData == nullptr)
1572
0
                        sArg.pfnProgress = nullptr;
1573
0
                    bool bOK =
1574
0
                        IRasterIO(eRWFlag, nXOff, nYOff, nBufXSize, nHalfHeight,
1575
0
                                  pabyData, nBufXSize, nHalfHeight, eBufType,
1576
0
                                  nBandCount, panBandMap, nPixelSpace,
1577
0
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
1578
0
                    GDALDestroyScaledProgress(sArg.pProgressData);
1579
1580
0
                    if (bOK)
1581
0
                    {
1582
0
                        sArg.pfnProgress = GDALScaledProgress;
1583
0
                        sArg.pProgressData = GDALCreateScaledProgress(
1584
0
                            0.5, 1, psExtraArg->pfnProgress,
1585
0
                            psExtraArg->pProgressData);
1586
0
                        if (sArg.pProgressData == nullptr)
1587
0
                            sArg.pfnProgress = nullptr;
1588
0
                        bOK = IRasterIO(eRWFlag, nXOff, nYOff + nHalfHeight,
1589
0
                                        nBufXSize, nBufYSize - nHalfHeight,
1590
0
                                        pabyData + nHalfHeight * nLineSpace,
1591
0
                                        nBufXSize, nBufYSize - nHalfHeight,
1592
0
                                        eBufType, nBandCount, panBandMap,
1593
0
                                        nPixelSpace, nLineSpace, nBandSpace,
1594
0
                                        &sArg) == CE_None;
1595
0
                        GDALDestroyScaledProgress(sArg.pProgressData);
1596
0
                    }
1597
0
                    return bOK ? CE_None : CE_Failure;
1598
0
                }
1599
0
                else if (nBufXSize >= 2)
1600
0
                {
1601
0
                    GDALRasterIOExtraArg sArg;
1602
0
                    INIT_RASTERIO_EXTRA_ARG(sArg);
1603
0
                    const int nHalfWidth = nBufXSize / 2;
1604
1605
0
                    sArg.pfnProgress = GDALScaledProgress;
1606
0
                    sArg.pProgressData = GDALCreateScaledProgress(
1607
0
                        0, 0.5, psExtraArg->pfnProgress,
1608
0
                        psExtraArg->pProgressData);
1609
0
                    if (sArg.pProgressData == nullptr)
1610
0
                        sArg.pfnProgress = nullptr;
1611
0
                    bool bOK =
1612
0
                        IRasterIO(eRWFlag, nXOff, nYOff, nHalfWidth, nBufYSize,
1613
0
                                  pabyData, nHalfWidth, nBufYSize, eBufType,
1614
0
                                  nBandCount, panBandMap, nPixelSpace,
1615
0
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
1616
0
                    GDALDestroyScaledProgress(sArg.pProgressData);
1617
1618
0
                    if (bOK)
1619
0
                    {
1620
0
                        sArg.pfnProgress = GDALScaledProgress;
1621
0
                        sArg.pProgressData = GDALCreateScaledProgress(
1622
0
                            0.5, 1, psExtraArg->pfnProgress,
1623
0
                            psExtraArg->pProgressData);
1624
0
                        if (sArg.pProgressData == nullptr)
1625
0
                            sArg.pfnProgress = nullptr;
1626
0
                        bOK = IRasterIO(eRWFlag, nXOff + nHalfWidth, nYOff,
1627
0
                                        nBufXSize - nHalfWidth, nBufYSize,
1628
0
                                        pabyData + nHalfWidth * nPixelSpace,
1629
0
                                        nBufXSize - nHalfWidth, nBufYSize,
1630
0
                                        eBufType, nBandCount, panBandMap,
1631
0
                                        nPixelSpace, nLineSpace, nBandSpace,
1632
0
                                        &sArg) == CE_None;
1633
0
                        GDALDestroyScaledProgress(sArg.pProgressData);
1634
0
                    }
1635
0
                    return bOK ? CE_None : CE_Failure;
1636
0
                }
1637
0
            }
1638
1639
0
            if (!ProcessRegion(nXOff, nYOff, nBufXSize, nBufYSize,
1640
0
                               psExtraArg->pfnProgress,
1641
0
                               psExtraArg->pProgressData))
1642
0
            {
1643
0
                return CE_Failure;
1644
0
            }
1645
0
            const auto eLastDT = m_aoSteps.back().eOutDT;
1646
0
            const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1647
0
            if (bIsBIPLike)
1648
0
            {
1649
0
                for (int iY = 0; iY < nBufYSize; ++iY)
1650
0
                {
1651
0
                    GDALCopyWords64(
1652
0
                        m_abyInput.data() + static_cast<size_t>(iY) * nBands *
1653
0
                                                nBufXSize * nLastDTSize,
1654
0
                        eLastDT, nLastDTSize, pabyData + iY * nLineSpace,
1655
0
                        eBufType, GDALGetDataTypeSizeBytes(eBufType),
1656
0
                        static_cast<size_t>(nBufXSize) * nBands);
1657
0
                }
1658
0
            }
1659
0
            else
1660
0
            {
1661
0
                CPLAssert(bIsBSQLike);
1662
0
                for (int iBand = 0; iBand < nBands; ++iBand)
1663
0
                {
1664
0
                    for (int iY = 0; iY < nBufYSize; ++iY)
1665
0
                    {
1666
0
                        GDALCopyWords64(
1667
0
                            m_abyInput.data() +
1668
0
                                (static_cast<size_t>(iY) * nBands * nBufXSize +
1669
0
                                 iBand) *
1670
0
                                    nLastDTSize,
1671
0
                            eLastDT, nLastDTSize * nBands,
1672
0
                            pabyData + iBand * nBandSpace + iY * nLineSpace,
1673
0
                            eBufType, nBufTypeSize, nBufXSize);
1674
0
                    }
1675
0
                }
1676
0
            }
1677
0
            return CE_None;
1678
0
        }
1679
0
    }
1680
1681
0
    return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1682
0
                                 nBufXSize, nBufYSize, eBufType, nBandCount,
1683
0
                                 panBandMap, nPixelSpace, nLineSpace,
1684
0
                                 nBandSpace, psExtraArg);
1685
0
}
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(
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
0
{
1747
0
    if (pszFuncName == nullptr || pszFuncName[0] == '\0')
1748
0
    {
1749
0
        CPLError(CE_Failure, CPLE_AppDefined,
1750
0
                 "pszFuncName should be non-empty");
1751
0
        return CE_Failure;
1752
0
    }
1753
1754
0
    auto &oMap = GetGlobalMapProcessedDatasetFunc();
1755
0
    if (oMap.find(pszFuncName) != oMap.end())
1756
0
    {
1757
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
1758
0
                 pszFuncName);
1759
0
        return CE_Failure;
1760
0
    }
1761
1762
0
    if (!pfnProcess)
1763
0
    {
1764
0
        CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
1765
0
        return CE_Failure;
1766
0
    }
1767
1768
0
    VRTProcessedDatasetFunc oFunc;
1769
0
    oFunc.osFuncName = pszFuncName;
1770
0
    oFunc.pUserData = pUserData;
1771
0
    if (pszXMLMetadata)
1772
0
    {
1773
0
        oFunc.bMetadataSpecified = true;
1774
0
        auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
1775
0
        if (!psTree)
1776
0
        {
1777
0
            CPLError(CE_Failure, CPLE_AppDefined,
1778
0
                     "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
1779
0
                     pszFuncName);
1780
0
            return CE_Failure;
1781
0
        }
1782
0
        const CPLXMLNode *psRoot = CPLGetXMLNode(
1783
0
            psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
1784
0
        if (!psRoot)
1785
0
        {
1786
0
            CPLError(CE_Failure, CPLE_AppDefined,
1787
0
                     "No root ProcessedDatasetFunctionArgumentsList element in "
1788
0
                     "pszXMLMetadata=%s for %s",
1789
0
                     pszXMLMetadata, pszFuncName);
1790
0
            return CE_Failure;
1791
0
        }
1792
0
        for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
1793
0
             psIter = psIter->psNext)
1794
0
        {
1795
0
            if (psIter->eType == CXT_Element &&
1796
0
                strcmp(psIter->pszValue, "Argument") == 0)
1797
0
            {
1798
0
                const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
1799
0
                if (!pszName)
1800
0
                {
1801
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1802
0
                             "Missing Argument.name attribute in "
1803
0
                             "pszXMLMetadata=%s for %s",
1804
0
                             pszXMLMetadata, pszFuncName);
1805
0
                    return CE_Failure;
1806
0
                }
1807
0
                const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
1808
0
                if (!pszType)
1809
0
                {
1810
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1811
0
                             "Missing Argument.type attribute in "
1812
0
                             "pszXMLMetadata=%s for %s",
1813
0
                             pszXMLMetadata, pszFuncName);
1814
0
                    return CE_Failure;
1815
0
                }
1816
0
                if (strcmp(pszType, "constant") == 0)
1817
0
                {
1818
0
                    const char *pszValue =
1819
0
                        CPLGetXMLValue(psIter, "value", nullptr);
1820
0
                    if (!pszValue)
1821
0
                    {
1822
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1823
0
                                 "Missing Argument.value attribute in "
1824
0
                                 "pszXMLMetadata=%s for %s",
1825
0
                                 pszXMLMetadata, pszFuncName);
1826
0
                        return CE_Failure;
1827
0
                    }
1828
0
                    oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
1829
0
                        pszValue;
1830
0
                }
1831
0
                else if (strcmp(pszType, "builtin") == 0)
1832
0
                {
1833
0
                    if (EQUAL(pszName, "nodata") ||
1834
0
                        EQUAL(pszName, "offset_{band}") ||
1835
0
                        EQUAL(pszName, "scale_{band}"))
1836
0
                    {
1837
0
                        oFunc.oSetBuiltinArguments.insert(
1838
0
                            CPLString(pszName).tolower());
1839
0
                    }
1840
0
                    else
1841
0
                    {
1842
0
                        CPLError(CE_Failure, CPLE_NotSupported,
1843
0
                                 "Unsupported builtin parameter name %s in "
1844
0
                                 "pszXMLMetadata=%s for %s. Only nodata, "
1845
0
                                 "offset_{band} and scale_{band} are supported",
1846
0
                                 pszName, pszXMLMetadata, pszFuncName);
1847
0
                        return CE_Failure;
1848
0
                    }
1849
0
                }
1850
0
                else if (strcmp(pszType, "boolean") == 0 ||
1851
0
                         strcmp(pszType, "string") == 0 ||
1852
0
                         strcmp(pszType, "integer") == 0 ||
1853
0
                         strcmp(pszType, "double") == 0 ||
1854
0
                         strcmp(pszType, "double_list") == 0)
1855
0
                {
1856
0
                    VRTProcessedDatasetFunc::OtherArgument otherArgument;
1857
0
                    otherArgument.bRequired = CPLTestBool(
1858
0
                        CPLGetXMLValue(psIter, "required", "false"));
1859
0
                    otherArgument.osType = pszType;
1860
0
                    oFunc.oOtherArguments[CPLString(pszName).tolower()] =
1861
0
                        std::move(otherArgument);
1862
0
                }
1863
0
                else
1864
0
                {
1865
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1866
0
                             "Unsupported type for parameter %s in "
1867
0
                             "pszXMLMetadata=%s for %s. Only boolean, string, "
1868
0
                             "integer, double and double_list are supported",
1869
0
                             pszName, pszXMLMetadata, pszFuncName);
1870
0
                    return CE_Failure;
1871
0
                }
1872
0
            }
1873
0
        }
1874
0
    }
1875
0
    oFunc.eRequestedInputDT = eRequestedInputDT;
1876
0
    if (nSupportedInputDTSize)
1877
0
    {
1878
0
        oFunc.aeSupportedInputDT.insert(
1879
0
            oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
1880
0
            paeSupportedInputDT + nSupportedInputDTSize);
1881
0
    }
1882
0
    if (nSupportedInputBandCountSize)
1883
0
    {
1884
0
        oFunc.anSupportedInputBandCount.insert(
1885
0
            oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
1886
0
            panSupportedInputBandCount + nSupportedInputBandCountSize);
1887
0
    }
1888
0
    oFunc.pfnInit = pfnInit;
1889
0
    oFunc.pfnFree = pfnFree;
1890
0
    oFunc.pfnProcess = pfnProcess;
1891
1892
0
    oMap[pszFuncName] = std::move(oFunc);
1893
1894
0
    return CE_None;
1895
0
}