Coverage Report

Created: 2025-06-22 06:59

/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_adfGeoTransform) == 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_adfGeoTransform[0] = poParentDS->m_adfGeoTransform[0];
408
0
        m_adfGeoTransform[1] = poParentDS->m_adfGeoTransform[1];
409
0
        m_adfGeoTransform[2] = poParentDS->m_adfGeoTransform[2];
410
0
        m_adfGeoTransform[3] = poParentDS->m_adfGeoTransform[3];
411
0
        m_adfGeoTransform[4] = poParentDS->m_adfGeoTransform[4];
412
0
        m_adfGeoTransform[5] = poParentDS->m_adfGeoTransform[5];
413
414
0
        m_adfGeoTransform[1] *=
415
0
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
416
0
        m_adfGeoTransform[2] *=
417
0
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
418
0
        m_adfGeoTransform[4] *=
419
0
            static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
420
0
        m_adfGeoTransform[5] *=
421
0
            static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
422
0
    }
423
424
0
    const CPLXMLNode *psOutputBands = CPLGetXMLNode(psTree, "OutputBands");
425
0
    if (psOutputBands)
426
0
    {
427
0
        if (const char *pszCount =
428
0
                CPLGetXMLValue(psOutputBands, "count", nullptr))
429
0
        {
430
0
            if (EQUAL(pszCount, "FROM_LAST_STEP"))
431
0
            {
432
0
                m_outputBandCountProvenance = ValueProvenance::FROM_LAST_STEP;
433
0
            }
434
0
            else if (!EQUAL(pszCount, "FROM_SOURCE"))
435
0
            {
436
0
                if (CPLGetValueType(pszCount) == CPL_VALUE_INTEGER)
437
0
                {
438
0
                    m_outputBandCountProvenance =
439
0
                        ValueProvenance::USER_PROVIDED;
440
0
                    m_outputBandCountValue = atoi(pszCount);
441
0
                    if (!GDALCheckBandCount(m_outputBandCountValue,
442
0
                                            /* bIsZeroAllowed = */ false))
443
0
                    {
444
                        // Error emitted by above function
445
0
                        return CE_Failure;
446
0
                    }
447
0
                }
448
0
                else
449
0
                {
450
0
                    CPLError(CE_Failure, CPLE_AppDefined,
451
0
                             "Invalid value for OutputBands.count");
452
0
                    return CE_Failure;
453
0
                }
454
0
            }
455
0
        }
456
457
0
        if (const char *pszType =
458
0
                CPLGetXMLValue(psOutputBands, "dataType", nullptr))
459
0
        {
460
0
            if (EQUAL(pszType, "FROM_LAST_STEP"))
461
0
            {
462
0
                m_outputBandDataTypeProvenance =
463
0
                    ValueProvenance::FROM_LAST_STEP;
464
0
            }
465
0
            else if (!EQUAL(pszType, "FROM_SOURCE"))
466
0
            {
467
0
                m_outputBandDataTypeProvenance = ValueProvenance::USER_PROVIDED;
468
0
                m_outputBandDataTypeValue = GDALGetDataTypeByName(pszType);
469
0
                if (m_outputBandDataTypeValue == GDT_Unknown)
470
0
                {
471
0
                    CPLError(CE_Failure, CPLE_AppDefined,
472
0
                             "Invalid value for OutputBands.dataType");
473
0
                    return CE_Failure;
474
0
                }
475
0
            }
476
0
        }
477
0
    }
478
0
    else if (CPLGetXMLNode(psTree, "VRTRasterBand"))
479
0
    {
480
0
        m_outputBandCountProvenance = ValueProvenance::FROM_VRTRASTERBAND;
481
0
        m_outputBandDataTypeProvenance = ValueProvenance::FROM_VRTRASTERBAND;
482
0
    }
483
484
0
    int nOutputBandCount = 0;
485
0
    switch (m_outputBandCountProvenance)
486
0
    {
487
0
        case ValueProvenance::USER_PROVIDED:
488
0
            nOutputBandCount = m_outputBandCountValue;
489
0
            break;
490
0
        case ValueProvenance::FROM_SOURCE:
491
0
            nOutputBandCount = m_poSrcDS->GetRasterCount();
492
0
            break;
493
0
        case ValueProvenance::FROM_VRTRASTERBAND:
494
0
            nOutputBandCount = nBands;
495
0
            break;
496
0
        case ValueProvenance::FROM_LAST_STEP:
497
0
            break;
498
0
    }
499
500
0
    const CPLXMLNode *psProcessingSteps =
501
0
        CPLGetXMLNode(psTree, "ProcessingSteps");
502
0
    if (!psProcessingSteps)
503
0
    {
504
0
        CPLError(CE_Failure, CPLE_AppDefined,
505
0
                 "ProcessingSteps element missing");
506
0
        return CE_Failure;
507
0
    }
508
509
0
    const auto eInDT = poSrcFirstBand->GetRasterDataType();
510
0
    for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
511
0
    {
512
0
        const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
513
0
        if (eDT != eInDT)
514
0
        {
515
0
            CPLError(CE_Warning, CPLE_AppDefined,
516
0
                     "Not all bands of the input dataset have the same data "
517
0
                     "type. The data type of the first band will be used as "
518
0
                     "the reference one.");
519
0
            break;
520
0
        }
521
0
    }
522
523
0
    GDALDataType eCurrentDT = eInDT;
524
0
    int nCurrentBandCount = m_poSrcDS->GetRasterCount();
525
526
0
    std::vector<double> adfNoData;
527
0
    for (int i = 1; i <= nCurrentBandCount; ++i)
528
0
    {
529
0
        int bHasVal = FALSE;
530
0
        const double dfVal =
531
0
            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
532
0
        adfNoData.emplace_back(
533
0
            bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
534
0
    }
535
536
0
    int nStepCount = 0;
537
0
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
538
0
         psStep = psStep->psNext)
539
0
    {
540
0
        if (psStep->eType == CXT_Element &&
541
0
            strcmp(psStep->pszValue, "Step") == 0)
542
0
        {
543
0
            ++nStepCount;
544
0
        }
545
0
    }
546
547
0
    int iStep = 0;
548
0
    for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
549
0
         psStep = psStep->psNext)
550
0
    {
551
0
        if (psStep->eType == CXT_Element &&
552
0
            strcmp(psStep->pszValue, "Step") == 0)
553
0
        {
554
0
            ++iStep;
555
0
            const bool bIsFinalStep = (iStep == nStepCount);
556
0
            std::vector<double> adfOutNoData;
557
0
            if (bIsFinalStep)
558
0
            {
559
                // Initialize adfOutNoData with nodata value of *output* bands
560
                // for final step
561
0
                if (m_outputBandCountProvenance ==
562
0
                    ValueProvenance::FROM_VRTRASTERBAND)
563
0
                {
564
0
                    for (int i = 1; i <= nBands; ++i)
565
0
                    {
566
0
                        int bHasVal = FALSE;
567
0
                        const double dfVal =
568
0
                            GetRasterBand(i)->GetNoDataValue(&bHasVal);
569
0
                        adfOutNoData.emplace_back(
570
0
                            bHasVal ? dfVal
571
0
                                    : std::numeric_limits<double>::quiet_NaN());
572
0
                    }
573
0
                }
574
0
                else if (m_outputBandCountProvenance ==
575
0
                         ValueProvenance::FROM_SOURCE)
576
0
                {
577
0
                    for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
578
0
                    {
579
0
                        int bHasVal = FALSE;
580
0
                        const double dfVal =
581
0
                            m_poSrcDS->GetRasterBand(i)->GetNoDataValue(
582
0
                                &bHasVal);
583
0
                        adfOutNoData.emplace_back(
584
0
                            bHasVal ? dfVal
585
0
                                    : std::numeric_limits<double>::quiet_NaN());
586
0
                    }
587
0
                }
588
0
                else if (m_outputBandCountProvenance ==
589
0
                         ValueProvenance::USER_PROVIDED)
590
0
                {
591
0
                    adfOutNoData.resize(
592
0
                        m_outputBandCountValue,
593
0
                        std::numeric_limits<double>::quiet_NaN());
594
0
                }
595
0
            }
596
0
            if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
597
0
                           adfNoData, adfOutNoData))
598
0
                return CE_Failure;
599
0
            adfNoData = std::move(adfOutNoData);
600
0
        }
601
0
    }
602
603
0
    if (m_aoSteps.empty())
604
0
    {
605
0
        CPLError(CE_Failure, CPLE_AppDefined,
606
0
                 "At least one step should be defined");
607
0
        return CE_Failure;
608
0
    }
609
610
0
    int nLargestInDTSizeTimesBand = 1;
611
0
    int nLargestOutDTSizeTimesBand = 1;
612
0
    for (const auto &oStep : m_aoSteps)
613
0
    {
614
0
        const int nInDTSizeTimesBand =
615
0
            GDALGetDataTypeSizeBytes(oStep.eInDT) * oStep.nInBands;
616
0
        nLargestInDTSizeTimesBand =
617
0
            std::max(nLargestInDTSizeTimesBand, nInDTSizeTimesBand);
618
0
        const int nOutDTSizeTimesBand =
619
0
            GDALGetDataTypeSizeBytes(oStep.eOutDT) * oStep.nOutBands;
620
0
        nLargestOutDTSizeTimesBand =
621
0
            std::max(nLargestOutDTSizeTimesBand, nOutDTSizeTimesBand);
622
0
    }
623
0
    m_nWorkingBytesPerPixel =
624
0
        nLargestInDTSizeTimesBand + nLargestOutDTSizeTimesBand;
625
626
    // Use only up to 40% of RAM to acquire source bands and generate the output
627
    // buffer.
628
0
    m_nAllowedRAMUsage = CPLGetUsablePhysicalRAM() / 10 * 4;
629
    // Only for tests now
630
0
    const char *pszMAX_RAM = "VRT_PROCESSED_DATASET_ALLOWED_RAM_USAGE";
631
0
    if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
632
0
    {
633
0
        CPL_IGNORE_RET_VAL(
634
0
            CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
635
0
    }
636
637
0
    if (m_nAllowedRAMUsage > 0)
638
0
    {
639
0
        bool bBlockSizeModified = false;
640
0
        while ((m_nBlockXSize >= 2 || m_nBlockYSize >= 2) &&
641
0
               static_cast<GIntBig>(m_nBlockXSize) * m_nBlockYSize >
642
0
                   m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
643
0
        {
644
0
            if ((m_nBlockXSize == nRasterXSize ||
645
0
                 m_nBlockYSize >= m_nBlockXSize) &&
646
0
                m_nBlockYSize >= 2)
647
0
            {
648
0
                m_nBlockYSize /= 2;
649
0
            }
650
0
            else
651
0
            {
652
0
                m_nBlockXSize /= 2;
653
0
            }
654
0
            bBlockSizeModified = true;
655
0
        }
656
0
        if (bBlockSizeModified)
657
0
        {
658
0
            if (bUserBlockSize)
659
0
            {
660
0
                CPLError(
661
0
                    CE_Warning, CPLE_AppDefined,
662
0
                    "Reducing block size to %d x %d to avoid consuming too "
663
0
                    "much RAM",
664
0
                    m_nBlockXSize, m_nBlockYSize);
665
0
            }
666
0
            else
667
0
            {
668
0
                CPLDebug(
669
0
                    "VRT",
670
0
                    "Reducing block size to %d x %d to avoid consuming too "
671
0
                    "much RAM",
672
0
                    m_nBlockXSize, m_nBlockYSize);
673
0
            }
674
0
        }
675
0
    }
676
677
0
    if (m_outputBandCountProvenance == ValueProvenance::FROM_LAST_STEP)
678
0
    {
679
0
        nOutputBandCount = nCurrentBandCount;
680
0
    }
681
0
    else if (nOutputBandCount != nCurrentBandCount)
682
0
    {
683
        // Should not happen frequently as pixel init functions are expected
684
        // to validate that they can accept the number of output bands provided
685
        // to them
686
0
        CPLError(
687
0
            CE_Failure, CPLE_AppDefined,
688
0
            "Number of output bands of last step (%d) is not consistent with "
689
0
            "number of VRTProcessedRasterBand's (%d)",
690
0
            nCurrentBandCount, nBands);
691
0
        return CE_Failure;
692
0
    }
693
694
0
    if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
695
0
    {
696
0
        m_outputBandDataTypeValue = eCurrentDT;
697
0
    }
698
699
0
    const auto ClearBands = [this]()
700
0
    {
701
0
        for (int i = 0; i < nBands; ++i)
702
0
            delete papoBands[i];
703
0
        CPLFree(papoBands);
704
0
        papoBands = nullptr;
705
0
        nBands = 0;
706
0
    };
707
708
0
    if (nBands != 0 &&
709
0
        (nBands != nOutputBandCount ||
710
0
         (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP &&
711
0
          m_outputBandDataTypeValue != papoBands[0]->GetRasterDataType())))
712
0
    {
713
0
        ClearBands();
714
0
    }
715
716
0
    const auto GetOutputBandType = [this, eCurrentDT](GDALDataType eSourceDT)
717
0
    {
718
0
        if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
719
0
            return eCurrentDT;
720
0
        else if (m_outputBandDataTypeProvenance ==
721
0
                 ValueProvenance::USER_PROVIDED)
722
0
            return m_outputBandDataTypeValue;
723
0
        else
724
0
            return eSourceDT;
725
0
    };
726
727
0
    if (m_outputBandCountProvenance == ValueProvenance::FROM_SOURCE)
728
0
    {
729
0
        for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
730
0
        {
731
0
            const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
732
0
            const GDALDataType eOutputBandType =
733
0
                GetOutputBandType(poSrcBand->GetRasterDataType());
734
0
            auto poBand =
735
0
                new VRTProcessedRasterBand(this, i + 1, eOutputBandType);
736
0
            poBand->CopyCommonInfoFrom(poSrcBand);
737
0
            SetBand(i + 1, poBand);
738
0
        }
739
0
    }
740
0
    else if (m_outputBandCountProvenance != ValueProvenance::FROM_VRTRASTERBAND)
741
0
    {
742
0
        const GDALDataType eOutputBandType = GetOutputBandType(eInDT);
743
744
0
        bool bClearAndSetBands = true;
745
0
        if (nBands == nOutputBandCount)
746
0
        {
747
0
            bClearAndSetBands = false;
748
0
            for (int i = 0; i < nBands; ++i)
749
0
            {
750
0
                bClearAndSetBands =
751
0
                    bClearAndSetBands ||
752
0
                    !dynamic_cast<VRTProcessedRasterBand *>(papoBands[i]) ||
753
0
                    papoBands[i]->GetRasterDataType() != eOutputBandType;
754
0
            }
755
0
        }
756
0
        if (bClearAndSetBands)
757
0
        {
758
0
            ClearBands();
759
0
            for (int i = 0; i < nOutputBandCount; ++i)
760
0
            {
761
0
                SetBand(i + 1, std::make_unique<VRTProcessedRasterBand>(
762
0
                                   this, i + 1, eOutputBandType));
763
0
            }
764
0
        }
765
0
    }
766
767
0
    if (nBands > 1)
768
0
        SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
769
770
0
    m_oXMLTree.reset(CPLCloneXMLTree(psTree));
771
772
0
    return CE_None;
773
0
}
774
775
/************************************************************************/
776
/*                            ParseStep()                               */
777
/************************************************************************/
778
779
/** Parse the current Step node and create a corresponding entry in m_aoSteps.
780
 *
781
 * @param psStep Step node
782
 * @param bIsFinalStep Whether this is the final step.
783
 * @param[in,out] eCurrentDT Input data type for this step.
784
 *                           Updated to output data type at end of method.
785
 * @param[in,out] nCurrentBandCount Input band count for this step.
786
 *                                  Updated to output band cout at end of
787
 *                                  method.
788
 * @param adfInNoData Input nodata values
789
 * @param[in,out] adfOutNoData Output nodata values, to be filled by this
790
 *                             method. When bIsFinalStep, this is also an
791
 *                             input parameter.
792
 * @return true on success.
793
 */
794
bool VRTProcessedDataset::ParseStep(const CPLXMLNode *psStep, bool bIsFinalStep,
795
                                    GDALDataType &eCurrentDT,
796
                                    int &nCurrentBandCount,
797
                                    std::vector<double> &adfInNoData,
798
                                    std::vector<double> &adfOutNoData)
799
0
{
800
0
    const char *pszStepName = CPLGetXMLValue(
801
0
        psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
802
0
    const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
803
0
    if (!pszAlgorithm)
804
0
    {
805
0
        CPLError(CE_Failure, CPLE_AppDefined,
806
0
                 "Step '%s' lacks a Algorithm element", pszStepName);
807
0
        return false;
808
0
    }
809
810
0
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
811
0
    const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
812
0
    if (oIterFunc == oMapFunctions.end())
813
0
    {
814
0
        CPLError(CE_Failure, CPLE_AppDefined,
815
0
                 "Step '%s' uses unregistered algorithm '%s'", pszStepName,
816
0
                 pszAlgorithm);
817
0
        return false;
818
0
    }
819
820
0
    const auto &oFunc = oIterFunc->second;
821
822
0
    if (!oFunc.aeSupportedInputDT.empty())
823
0
    {
824
0
        if (std::find(oFunc.aeSupportedInputDT.begin(),
825
0
                      oFunc.aeSupportedInputDT.end(),
826
0
                      eCurrentDT) == oFunc.aeSupportedInputDT.end())
827
0
        {
828
0
            CPLError(CE_Failure, CPLE_AppDefined,
829
0
                     "Step '%s' (using algorithm '%s') does not "
830
0
                     "support input data type = '%s'",
831
0
                     pszStepName, pszAlgorithm,
832
0
                     GDALGetDataTypeName(eCurrentDT));
833
0
            return false;
834
0
        }
835
0
    }
836
837
0
    if (!oFunc.anSupportedInputBandCount.empty())
838
0
    {
839
0
        if (std::find(oFunc.anSupportedInputBandCount.begin(),
840
0
                      oFunc.anSupportedInputBandCount.end(),
841
0
                      nCurrentBandCount) ==
842
0
            oFunc.anSupportedInputBandCount.end())
843
0
        {
844
0
            CPLError(CE_Failure, CPLE_AppDefined,
845
0
                     "Step '%s' (using algorithm '%s') does not "
846
0
                     "support input band count = %d",
847
0
                     pszStepName, pszAlgorithm, nCurrentBandCount);
848
0
            return false;
849
0
        }
850
0
    }
851
852
0
    Step oStep;
853
0
    oStep.osAlgorithm = pszAlgorithm;
854
0
    oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
855
0
                      ? oFunc.eRequestedInputDT
856
0
                      : eCurrentDT;
857
0
    oStep.nInBands = nCurrentBandCount;
858
859
    // Unless modified by pfnInit...
860
0
    oStep.eOutDT = oStep.eInDT;
861
862
0
    oStep.adfInNoData = adfInNoData;
863
0
    oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
864
865
    // Deal with constant arguments
866
0
    for (const auto &nameValuePair : oFunc.oMapConstantArguments)
867
0
    {
868
0
        oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
869
0
                                        nameValuePair.second.c_str());
870
0
    }
871
872
    // Deal with built-in arguments
873
0
    if (oFunc.oSetBuiltinArguments.find("nodata") !=
874
0
        oFunc.oSetBuiltinArguments.end())
875
0
    {
876
0
        int bHasVal = false;
877
0
        const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
878
0
        const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
879
0
        if (bHasVal)
880
0
        {
881
0
            oStep.aosArguments.AddNameValue("nodata",
882
0
                                            CPLSPrintf("%.17g", dfVal));
883
0
        }
884
0
    }
885
886
0
    if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
887
0
        oFunc.oSetBuiltinArguments.end())
888
0
    {
889
0
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
890
0
        {
891
0
            int bHasVal = false;
892
0
            const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
893
0
            oStep.aosArguments.AddNameValue(
894
0
                CPLSPrintf("offset_%d", i),
895
0
                CPLSPrintf("%.17g", bHasVal ? dfVal : 0.0));
896
0
        }
897
0
    }
898
899
0
    if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
900
0
        oFunc.oSetBuiltinArguments.end())
901
0
    {
902
0
        for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
903
0
        {
904
0
            int bHasVal = false;
905
0
            const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
906
0
            oStep.aosArguments.AddNameValue(
907
0
                CPLSPrintf("scale_%d", i),
908
0
                CPLSPrintf("%.17g", bHasVal ? dfVal : 1.0));
909
0
        }
910
0
    }
911
912
    // Parse arguments specified in VRT
913
0
    std::set<std::string> oFoundArguments;
914
915
0
    for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
916
0
         psStepChild = psStepChild->psNext)
917
0
    {
918
0
        if (psStepChild->eType == CXT_Element &&
919
0
            strcmp(psStepChild->pszValue, "Argument") == 0)
920
0
        {
921
0
            const char *pszParamName =
922
0
                CPLGetXMLValue(psStepChild, "name", nullptr);
923
0
            if (!pszParamName)
924
0
            {
925
0
                CPLError(CE_Failure, CPLE_AppDefined,
926
0
                         "Step '%s' has a Argument without a name attribute",
927
0
                         pszStepName);
928
0
                return false;
929
0
            }
930
0
            const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
931
0
            auto oOtherArgIter =
932
0
                oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
933
0
            if (!oFunc.oOtherArguments.empty() &&
934
0
                oOtherArgIter == oFunc.oOtherArguments.end())
935
0
            {
936
                // If we got a parameter name like 'coefficients_1',
937
                // try to fetch the generic 'coefficients_{band}'
938
0
                std::string osParamName(pszParamName);
939
0
                const auto nPos = osParamName.rfind('_');
940
0
                if (nPos != std::string::npos)
941
0
                {
942
0
                    osParamName.resize(nPos + 1);
943
0
                    osParamName += "{band}";
944
0
                    oOtherArgIter = oFunc.oOtherArguments.find(
945
0
                        CPLString(osParamName).tolower());
946
0
                }
947
0
            }
948
0
            if (oOtherArgIter != oFunc.oOtherArguments.end())
949
0
            {
950
0
                oFoundArguments.insert(oOtherArgIter->first);
951
952
0
                const std::string &osType = oOtherArgIter->second.osType;
953
0
                if (osType == "boolean")
954
0
                {
955
0
                    if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
956
0
                    {
957
0
                        CPLError(CE_Failure, CPLE_NotSupported,
958
0
                                 "Step '%s' has a Argument '%s' whose "
959
0
                                 "value '%s' is not a boolean",
960
0
                                 pszStepName, pszParamName, pszValue);
961
0
                        return false;
962
0
                    }
963
0
                }
964
0
                else if (osType == "integer")
965
0
                {
966
0
                    if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
967
0
                    {
968
0
                        CPLError(CE_Failure, CPLE_NotSupported,
969
0
                                 "Step '%s' has a Argument '%s' whose "
970
0
                                 "value '%s' is not a integer",
971
0
                                 pszStepName, pszParamName, pszValue);
972
0
                        return false;
973
0
                    }
974
0
                }
975
0
                else if (osType == "double")
976
0
                {
977
0
                    const auto eType = CPLGetValueType(pszValue);
978
0
                    if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
979
0
                    {
980
0
                        CPLError(CE_Failure, CPLE_NotSupported,
981
0
                                 "Step '%s' has a Argument '%s' whose "
982
0
                                 "value '%s' is not a double",
983
0
                                 pszStepName, pszParamName, pszValue);
984
0
                        return false;
985
0
                    }
986
0
                }
987
0
                else if (osType == "double_list")
988
0
                {
989
0
                    const CPLStringList aosTokens(
990
0
                        CSLTokenizeString2(pszValue, ",", 0));
991
0
                    for (int i = 0; i < aosTokens.size(); ++i)
992
0
                    {
993
0
                        const auto eType = CPLGetValueType(aosTokens[i]);
994
0
                        if (eType != CPL_VALUE_INTEGER &&
995
0
                            eType != CPL_VALUE_REAL)
996
0
                        {
997
0
                            CPLError(CE_Failure, CPLE_NotSupported,
998
0
                                     "Step '%s' has a Argument '%s' "
999
0
                                     "whose value '%s' is not a "
1000
0
                                     "comma-separated list of doubles",
1001
0
                                     pszStepName, pszParamName, pszValue);
1002
0
                            return false;
1003
0
                        }
1004
0
                    }
1005
0
                }
1006
0
                else if (osType != "string")
1007
0
                {
1008
0
                    CPLDebug("VRT", "Unhandled argument type '%s'",
1009
0
                             osType.c_str());
1010
0
                    CPLAssert(0);
1011
0
                }
1012
0
            }
1013
0
            else if (oFunc.bMetadataSpecified &&
1014
0
                     oFunc.oSetBuiltinArguments.find(
1015
0
                         CPLString(pszParamName).tolower()) ==
1016
0
                         oFunc.oSetBuiltinArguments.end() &&
1017
0
                     oFunc.oMapConstantArguments.find(
1018
0
                         CPLString(pszParamName).tolower()) ==
1019
0
                         oFunc.oMapConstantArguments.end())
1020
0
            {
1021
0
                CPLError(CE_Warning, CPLE_NotSupported,
1022
0
                         "Step '%s' has a Argument '%s' which is not "
1023
0
                         "supported",
1024
0
                         pszStepName, pszParamName);
1025
0
            }
1026
1027
0
            oStep.aosArguments.AddNameValue(pszParamName, pszValue);
1028
0
        }
1029
0
    }
1030
1031
    // Check that required arguments have been specified
1032
0
    for (const auto &oIter : oFunc.oOtherArguments)
1033
0
    {
1034
0
        if (oIter.second.bRequired &&
1035
0
            oFoundArguments.find(oIter.first) == oFoundArguments.end())
1036
0
        {
1037
0
            CPLError(CE_Failure, CPLE_AppDefined,
1038
0
                     "Step '%s' lacks required Argument '%s'", pszStepName,
1039
0
                     oIter.first.c_str());
1040
0
            return false;
1041
0
        }
1042
0
    }
1043
1044
0
    if (oFunc.pfnInit)
1045
0
    {
1046
0
        double *padfOutNoData = nullptr;
1047
0
        if (bIsFinalStep && !adfOutNoData.empty())
1048
0
        {
1049
0
            oStep.nOutBands = static_cast<int>(adfOutNoData.size());
1050
0
            padfOutNoData = static_cast<double *>(
1051
0
                CPLMalloc(adfOutNoData.size() * sizeof(double)));
1052
0
            memcpy(padfOutNoData, adfOutNoData.data(),
1053
0
                   adfOutNoData.size() * sizeof(double));
1054
0
        }
1055
0
        else
1056
0
        {
1057
0
            oStep.nOutBands = 0;
1058
0
        }
1059
1060
0
        if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
1061
0
                          oStep.aosArguments.List(), oStep.nInBands,
1062
0
                          oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
1063
0
                          &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
1064
0
                          &(oStep.pWorkingData)) != CE_None)
1065
0
        {
1066
0
            CPLError(CE_Failure, CPLE_AppDefined,
1067
0
                     "Step '%s' (using algorithm '%s') init() function "
1068
0
                     "failed",
1069
0
                     pszStepName, pszAlgorithm);
1070
0
            CPLFree(padfOutNoData);
1071
0
            return false;
1072
0
        }
1073
1074
        // Input nodata values may have been modified by pfnInit()
1075
0
        oStep.adfInNoData = adfInNoData;
1076
1077
0
        if (padfOutNoData)
1078
0
        {
1079
0
            adfOutNoData = std::vector<double>(padfOutNoData,
1080
0
                                               padfOutNoData + oStep.nOutBands);
1081
0
        }
1082
0
        else
1083
0
        {
1084
0
            adfOutNoData = std::vector<double>(
1085
0
                oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
1086
0
        }
1087
0
        CPLFree(padfOutNoData);
1088
1089
0
        oStep.adfOutNoData = adfOutNoData;
1090
0
    }
1091
0
    else
1092
0
    {
1093
0
        oStep.nOutBands = oStep.nInBands;
1094
0
        adfOutNoData = oStep.adfOutNoData;
1095
0
    }
1096
1097
0
    eCurrentDT = oStep.eOutDT;
1098
0
    nCurrentBandCount = oStep.nOutBands;
1099
1100
0
    m_aoSteps.emplace_back(std::move(oStep));
1101
1102
0
    return true;
1103
0
}
1104
1105
/************************************************************************/
1106
/*                           SerializeToXML()                           */
1107
/************************************************************************/
1108
1109
CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
1110
1111
0
{
1112
0
    CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
1113
0
    if (psTree == nullptr)
1114
0
        return psTree;
1115
1116
    /* -------------------------------------------------------------------- */
1117
    /*      Remove VRTRasterBand nodes from the original tree and find the  */
1118
    /*      last child.                                                     */
1119
    /* -------------------------------------------------------------------- */
1120
0
    CPLXMLNode *psLastChild = psTree->psChild;
1121
0
    CPLXMLNode *psPrevChild = nullptr;
1122
0
    while (psLastChild)
1123
0
    {
1124
0
        CPLXMLNode *psNextChild = psLastChild->psNext;
1125
0
        if (psLastChild->eType == CXT_Element &&
1126
0
            strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
1127
0
        {
1128
0
            if (psPrevChild)
1129
0
                psPrevChild->psNext = psNextChild;
1130
0
            else
1131
0
                psTree->psChild = psNextChild;
1132
0
            psLastChild->psNext = nullptr;
1133
0
            CPLDestroyXMLNode(psLastChild);
1134
0
            psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
1135
0
        }
1136
0
        else if (!psNextChild)
1137
0
        {
1138
0
            break;
1139
0
        }
1140
0
        else
1141
0
        {
1142
0
            psPrevChild = psLastChild;
1143
0
            psLastChild = psNextChild;
1144
0
        }
1145
0
    }
1146
0
    CPLAssert(psLastChild);  // we have at least Input
1147
1148
    /* -------------------------------------------------------------------- */
1149
    /*      Serialize bands.                                                */
1150
    /* -------------------------------------------------------------------- */
1151
0
    bool bHasWarnedAboutRAMUsage = false;
1152
0
    size_t nAccRAMUsage = 0;
1153
0
    for (int iBand = 0; iBand < nBands; iBand++)
1154
0
    {
1155
0
        CPLXMLNode *psBandTree =
1156
0
            static_cast<VRTRasterBand *>(papoBands[iBand])
1157
0
                ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
1158
0
                                 nAccRAMUsage);
1159
1160
0
        if (psBandTree != nullptr)
1161
0
        {
1162
0
            psLastChild->psNext = psBandTree;
1163
0
            psLastChild = psBandTree;
1164
0
        }
1165
0
    }
1166
1167
0
    return psTree;
1168
0
}
1169
1170
/************************************************************************/
1171
/*                           SerializeToXML()                           */
1172
/************************************************************************/
1173
1174
CPLXMLNode *
1175
VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
1176
                                       bool &bHasWarnedAboutRAMUsage,
1177
                                       size_t &nAccRAMUsage)
1178
1179
0
{
1180
0
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1181
0
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1182
1183
    /* -------------------------------------------------------------------- */
1184
    /*      Set subclass.                                                   */
1185
    /* -------------------------------------------------------------------- */
1186
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1187
0
                     CXT_Text, "VRTProcessedRasterBand");
1188
1189
0
    return psTree;
1190
0
}
1191
1192
/************************************************************************/
1193
/*                            GetBlockSize()                            */
1194
/************************************************************************/
1195
1196
/** Return block size */
1197
void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
1198
                                       int *pnBlockYSize) const
1199
1200
0
{
1201
0
    *pnBlockXSize = m_nBlockXSize;
1202
0
    *pnBlockYSize = m_nBlockYSize;
1203
0
}
1204
1205
/************************************************************************/
1206
/*                            ProcessRegion()                           */
1207
/************************************************************************/
1208
1209
/** Compute pixel values for the specified region.
1210
 *
1211
 * The output is stored in m_abyInput in a pixel-interleaved way.
1212
 */
1213
bool VRTProcessedDataset::ProcessRegion(int nXOff, int nYOff, int nBufXSize,
1214
                                        int nBufYSize,
1215
                                        GDALProgressFunc pfnProgress,
1216
                                        void *pProgressData)
1217
0
{
1218
1219
0
    CPLAssert(!m_aoSteps.empty());
1220
1221
0
    const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
1222
1223
0
    const int nFirstBandCount = m_aoSteps.front().nInBands;
1224
0
    CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
1225
0
    const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
1226
0
    const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
1227
0
    auto &abyInput = m_abyInput;
1228
0
    auto &abyOutput = m_abyOutput;
1229
1230
0
    const char *pszInterleave =
1231
0
        m_poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
1232
0
    if (nFirstBandCount > 1 && (!pszInterleave || EQUAL(pszInterleave, "BAND")))
1233
0
    {
1234
        // If there are several bands and the source dataset organization
1235
        // is apparently band interleaved, then first acquire data in
1236
        // a BSQ organization in the abyInput array use in the native
1237
        // data type.
1238
        // And then transpose it and convert it to the expected data type
1239
        // of the first step.
1240
0
        const auto eSrcDT = m_poSrcDS->GetRasterBand(1)->GetRasterDataType();
1241
0
        try
1242
0
        {
1243
0
            abyInput.resize(nPixelCount * nFirstBandCount *
1244
0
                            GDALGetDataTypeSizeBytes(eSrcDT));
1245
0
        }
1246
0
        catch (const std::bad_alloc &)
1247
0
        {
1248
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1249
0
                     "Out of memory allocating working buffer");
1250
0
            return false;
1251
0
        }
1252
1253
0
        try
1254
0
        {
1255
0
            abyOutput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1256
0
        }
1257
0
        catch (const std::bad_alloc &)
1258
0
        {
1259
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1260
0
                     "Out of memory allocating working buffer");
1261
0
            return false;
1262
0
        }
1263
1264
0
        GDALRasterIOExtraArg sArg;
1265
0
        INIT_RASTERIO_EXTRA_ARG(sArg);
1266
0
        sArg.pfnProgress = GDALScaledProgress;
1267
0
        sArg.pProgressData =
1268
0
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1269
0
        if (sArg.pProgressData == nullptr)
1270
0
            sArg.pfnProgress = nullptr;
1271
1272
0
        CPLDebugOnly("VRT", "ProcessRegion(): start RasterIO()");
1273
0
        const bool bOK =
1274
0
            m_poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nBufXSize, nBufYSize,
1275
0
                                abyInput.data(), nBufXSize, nBufYSize, eSrcDT,
1276
0
                                nFirstBandCount, nullptr, 0, 0, 0,
1277
0
                                &sArg) == CE_None;
1278
0
        CPLDebugOnly("VRT", "ProcessRegion(): end RasterIO()");
1279
0
        GDALDestroyScaledProgress(sArg.pProgressData);
1280
0
        if (!bOK)
1281
0
            return false;
1282
1283
0
        CPLDebugOnly("VRT", "ProcessRegion(): start GDALTranspose2D()");
1284
0
        GDALTranspose2D(abyInput.data(), eSrcDT, abyOutput.data(), eFirstDT,
1285
0
                        static_cast<size_t>(nBufXSize) * nBufYSize,
1286
0
                        nFirstBandCount);
1287
0
        CPLDebugOnly("VRT", "ProcessRegion(): end GDALTranspose2D()");
1288
1289
        // Swap arrays
1290
0
        std::swap(abyInput, abyOutput);
1291
0
    }
1292
0
    else
1293
0
    {
1294
0
        try
1295
0
        {
1296
0
            abyInput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1297
0
        }
1298
0
        catch (const std::bad_alloc &)
1299
0
        {
1300
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1301
0
                     "Out of memory allocating working buffer");
1302
0
            return false;
1303
0
        }
1304
1305
0
        GDALRasterIOExtraArg sArg;
1306
0
        INIT_RASTERIO_EXTRA_ARG(sArg);
1307
0
        sArg.pfnProgress = GDALScaledProgress;
1308
0
        sArg.pProgressData =
1309
0
            GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1310
0
        if (sArg.pProgressData == nullptr)
1311
0
            sArg.pfnProgress = nullptr;
1312
1313
0
        const bool bOK =
1314
0
            m_poSrcDS->RasterIO(
1315
0
                GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
1316
0
                nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
1317
0
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
1318
0
                static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount *
1319
0
                    nBufXSize,
1320
0
                nFirstDTSize, &sArg) == CE_None;
1321
1322
0
        GDALDestroyScaledProgress(sArg.pProgressData);
1323
0
        if (!bOK)
1324
0
            return false;
1325
0
    }
1326
1327
0
    const double dfSrcXOff = nXOff;
1328
0
    const double dfSrcYOff = nYOff;
1329
0
    const double dfSrcXSize = nBufXSize;
1330
0
    const double dfSrcYSize = nBufYSize;
1331
1332
0
    double adfSrcGT[6];
1333
0
    if (m_poSrcDS->GetGeoTransform(adfSrcGT) != CE_None)
1334
0
    {
1335
0
        adfSrcGT[0] = 0;
1336
0
        adfSrcGT[1] = 1;
1337
0
        adfSrcGT[2] = 0;
1338
0
        adfSrcGT[3] = 0;
1339
0
        adfSrcGT[4] = 0;
1340
0
        adfSrcGT[5] = 1;
1341
0
    }
1342
1343
0
    GDALDataType eLastDT = eFirstDT;
1344
0
    const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
1345
1346
0
    int iStep = 0;
1347
0
    for (const auto &oStep : m_aoSteps)
1348
0
    {
1349
0
        const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
1350
0
        CPLAssert(oIterFunc != oMapFunctions.end());
1351
1352
        // Data type adaptation
1353
0
        if (eLastDT != oStep.eInDT)
1354
0
        {
1355
0
            try
1356
0
            {
1357
0
                abyOutput.resize(nPixelCount * oStep.nInBands *
1358
0
                                 GDALGetDataTypeSizeBytes(oStep.eInDT));
1359
0
            }
1360
0
            catch (const std::bad_alloc &)
1361
0
            {
1362
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
1363
0
                         "Out of memory allocating working buffer");
1364
0
                return false;
1365
0
            }
1366
1367
0
            GDALCopyWords64(abyInput.data(), eLastDT,
1368
0
                            GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
1369
0
                            oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
1370
0
                            nPixelCount * oStep.nInBands);
1371
1372
0
            std::swap(abyInput, abyOutput);
1373
0
        }
1374
1375
0
        try
1376
0
        {
1377
0
            abyOutput.resize(nPixelCount * oStep.nOutBands *
1378
0
                             GDALGetDataTypeSizeBytes(oStep.eOutDT));
1379
0
        }
1380
0
        catch (const std::bad_alloc &)
1381
0
        {
1382
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1383
0
                     "Out of memory allocating working buffer");
1384
0
            return false;
1385
0
        }
1386
1387
0
        const auto &oFunc = oIterFunc->second;
1388
0
        if (oFunc.pfnProcess(
1389
0
                oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
1390
0
                oStep.aosArguments.List(), nBufXSize, nBufYSize,
1391
0
                abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
1392
0
                oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
1393
0
                oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
1394
0
                dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, adfSrcGT,
1395
0
                m_osVRTPath.c_str(),
1396
0
                /*papszExtra=*/nullptr) != CE_None)
1397
0
        {
1398
0
            return false;
1399
0
        }
1400
1401
0
        std::swap(abyInput, abyOutput);
1402
0
        eLastDT = oStep.eOutDT;
1403
1404
0
        ++iStep;
1405
0
        if (pfnProgress &&
1406
0
            !pfnProgress(0.5 + 0.5 * iStep / static_cast<int>(m_aoSteps.size()),
1407
0
                         "", pProgressData))
1408
0
            return false;
1409
0
    }
1410
1411
0
    return true;
1412
0
}
1413
1414
/************************************************************************/
1415
/*                        VRTProcessedRasterBand()                      */
1416
/************************************************************************/
1417
1418
/** Constructor */
1419
VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
1420
                                               int nBandIn,
1421
                                               GDALDataType eDataTypeIn)
1422
0
{
1423
0
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
1424
1425
0
    poDS = poDSIn;
1426
0
    nBand = nBandIn;
1427
0
    eAccess = GA_Update;
1428
0
    eDataType = eDataTypeIn;
1429
1430
0
    poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
1431
0
}
1432
1433
/************************************************************************/
1434
/*                           GetOverviewCount()                         */
1435
/************************************************************************/
1436
1437
int VRTProcessedRasterBand::GetOverviewCount()
1438
0
{
1439
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1440
0
    return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
1441
0
}
1442
1443
/************************************************************************/
1444
/*                              GetOverview()                           */
1445
/************************************************************************/
1446
1447
GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
1448
0
{
1449
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1450
0
    if (iOvr < 0 ||
1451
0
        iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
1452
0
        return nullptr;
1453
0
    return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
1454
0
}
1455
1456
/************************************************************************/
1457
/*                             IReadBlock()                             */
1458
/************************************************************************/
1459
1460
CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1461
                                          void *pImage)
1462
1463
0
{
1464
0
    auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1465
1466
0
    int nBufXSize = 0;
1467
0
    int nBufYSize = 0;
1468
0
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
1469
1470
0
    const int nXPixelOff = nBlockXOff * nBlockXSize;
1471
0
    const int nYPixelOff = nBlockYOff * nBlockYSize;
1472
0
    if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize,
1473
0
                                nullptr, nullptr))
1474
0
    {
1475
0
        return CE_Failure;
1476
0
    }
1477
1478
0
    const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
1479
0
    CPLAssert(nOutBands == poVRTDS->GetRasterCount());
1480
0
    const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
1481
0
    const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1482
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1483
1484
    // Dispatch final output buffer to cached blocks of output bands
1485
0
    for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
1486
0
    {
1487
0
        GDALRasterBlock *poBlock = nullptr;
1488
0
        GByte *pDst;
1489
0
        if (iDstBand + 1 == nBand)
1490
0
        {
1491
0
            pDst = static_cast<GByte *>(pImage);
1492
0
        }
1493
0
        else
1494
0
        {
1495
0
            auto poOtherBand = poVRTDS->papoBands[iDstBand];
1496
0
            poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1497
0
            if (poBlock)
1498
0
            {
1499
0
                poBlock->DropLock();
1500
0
                continue;
1501
0
            }
1502
0
            poBlock = poOtherBand->GetLockedBlockRef(
1503
0
                nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
1504
0
            if (!poBlock)
1505
0
                continue;
1506
0
            pDst = static_cast<GByte *>(poBlock->GetDataRef());
1507
0
        }
1508
0
        for (int iY = 0; iY < nBufYSize; ++iY)
1509
0
        {
1510
0
            GDALCopyWords64(poVRTDS->m_abyInput.data() +
1511
0
                                (iDstBand + static_cast<size_t>(iY) *
1512
0
                                                nBufXSize * nOutBands) *
1513
0
                                    nLastDTSize,
1514
0
                            eLastDT, nLastDTSize * nOutBands,
1515
0
                            pDst +
1516
0
                                static_cast<size_t>(iY) * nBlockXSize * nDTSize,
1517
0
                            eDataType, nDTSize, nBufXSize);
1518
0
        }
1519
0
        if (poBlock)
1520
0
            poBlock->DropLock();
1521
0
    }
1522
1523
0
    return CE_None;
1524
0
}
1525
1526
/************************************************************************/
1527
/*                VRTProcessedDataset::IRasterIO()                      */
1528
/************************************************************************/
1529
1530
CPLErr VRTProcessedDataset::IRasterIO(
1531
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1532
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1533
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1534
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1535
0
{
1536
    // Try to pass the request to the most appropriate overview dataset.
1537
0
    if (nBufXSize < nXSize && nBufYSize < nYSize)
1538
0
    {
1539
0
        int bTried = FALSE;
1540
0
        const CPLErr eErr = TryOverviewRasterIO(
1541
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1542
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1543
0
            nBandSpace, psExtraArg, &bTried);
1544
0
        if (bTried)
1545
0
            return eErr;
1546
0
    }
1547
1548
    // Optimize reading of all bands at nominal resolution for BIP-like or
1549
    // BSQ-like buffer spacing.
1550
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
1551
0
        nBandCount == nBands)
1552
0
    {
1553
0
        const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1554
0
        const bool bIsBIPLike = nBandSpace == nBufTypeSize &&
1555
0
                                nPixelSpace == nBandSpace * nBands &&
1556
0
                                nLineSpace >= nPixelSpace * nBufXSize &&
1557
0
                                IsAllBands(nBandCount, panBandMap);
1558
0
        const bool bIsBSQLike = nPixelSpace == nBufTypeSize &&
1559
0
                                nLineSpace >= nPixelSpace * nBufXSize &&
1560
0
                                nBandSpace >= nLineSpace * nBufYSize &&
1561
0
                                IsAllBands(nBandCount, panBandMap);
1562
0
        if (bIsBIPLike || bIsBSQLike)
1563
0
        {
1564
0
            GByte *pabyData = static_cast<GByte *>(pData);
1565
            // If acquiring the region of interest in a single time is going
1566
            // to consume too much RAM, split in halves.
1567
0
            if (m_nAllowedRAMUsage > 0 &&
1568
0
                static_cast<GIntBig>(nBufXSize) * nBufYSize >
1569
0
                    m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
1570
0
            {
1571
0
                if ((nBufXSize == nRasterXSize || nBufYSize >= nBufXSize) &&
1572
0
                    nBufYSize >= 2)
1573
0
                {
1574
0
                    GDALRasterIOExtraArg sArg;
1575
0
                    INIT_RASTERIO_EXTRA_ARG(sArg);
1576
0
                    const int nHalfHeight = nBufYSize / 2;
1577
1578
0
                    sArg.pfnProgress = GDALScaledProgress;
1579
0
                    sArg.pProgressData = GDALCreateScaledProgress(
1580
0
                        0, 0.5, psExtraArg->pfnProgress,
1581
0
                        psExtraArg->pProgressData);
1582
0
                    if (sArg.pProgressData == nullptr)
1583
0
                        sArg.pfnProgress = nullptr;
1584
0
                    bool bOK =
1585
0
                        IRasterIO(eRWFlag, nXOff, nYOff, nBufXSize, nHalfHeight,
1586
0
                                  pabyData, nBufXSize, nHalfHeight, eBufType,
1587
0
                                  nBandCount, panBandMap, nPixelSpace,
1588
0
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
1589
0
                    GDALDestroyScaledProgress(sArg.pProgressData);
1590
1591
0
                    if (bOK)
1592
0
                    {
1593
0
                        sArg.pfnProgress = GDALScaledProgress;
1594
0
                        sArg.pProgressData = GDALCreateScaledProgress(
1595
0
                            0.5, 1, psExtraArg->pfnProgress,
1596
0
                            psExtraArg->pProgressData);
1597
0
                        if (sArg.pProgressData == nullptr)
1598
0
                            sArg.pfnProgress = nullptr;
1599
0
                        bOK = IRasterIO(eRWFlag, nXOff, nYOff + nHalfHeight,
1600
0
                                        nBufXSize, nBufYSize - nHalfHeight,
1601
0
                                        pabyData + nHalfHeight * nLineSpace,
1602
0
                                        nBufXSize, nBufYSize - nHalfHeight,
1603
0
                                        eBufType, nBandCount, panBandMap,
1604
0
                                        nPixelSpace, nLineSpace, nBandSpace,
1605
0
                                        &sArg) == CE_None;
1606
0
                        GDALDestroyScaledProgress(sArg.pProgressData);
1607
0
                    }
1608
0
                    return bOK ? CE_None : CE_Failure;
1609
0
                }
1610
0
                else if (nBufXSize >= 2)
1611
0
                {
1612
0
                    GDALRasterIOExtraArg sArg;
1613
0
                    INIT_RASTERIO_EXTRA_ARG(sArg);
1614
0
                    const int nHalfWidth = nBufXSize / 2;
1615
1616
0
                    sArg.pfnProgress = GDALScaledProgress;
1617
0
                    sArg.pProgressData = GDALCreateScaledProgress(
1618
0
                        0, 0.5, psExtraArg->pfnProgress,
1619
0
                        psExtraArg->pProgressData);
1620
0
                    if (sArg.pProgressData == nullptr)
1621
0
                        sArg.pfnProgress = nullptr;
1622
0
                    bool bOK =
1623
0
                        IRasterIO(eRWFlag, nXOff, nYOff, nHalfWidth, nBufYSize,
1624
0
                                  pabyData, nHalfWidth, nBufYSize, eBufType,
1625
0
                                  nBandCount, panBandMap, nPixelSpace,
1626
0
                                  nLineSpace, nBandSpace, &sArg) == CE_None;
1627
0
                    GDALDestroyScaledProgress(sArg.pProgressData);
1628
1629
0
                    if (bOK)
1630
0
                    {
1631
0
                        sArg.pfnProgress = GDALScaledProgress;
1632
0
                        sArg.pProgressData = GDALCreateScaledProgress(
1633
0
                            0.5, 1, psExtraArg->pfnProgress,
1634
0
                            psExtraArg->pProgressData);
1635
0
                        if (sArg.pProgressData == nullptr)
1636
0
                            sArg.pfnProgress = nullptr;
1637
0
                        bOK = IRasterIO(eRWFlag, nXOff + nHalfWidth, nYOff,
1638
0
                                        nBufXSize - nHalfWidth, nBufYSize,
1639
0
                                        pabyData + nHalfWidth * nPixelSpace,
1640
0
                                        nBufXSize - nHalfWidth, nBufYSize,
1641
0
                                        eBufType, nBandCount, panBandMap,
1642
0
                                        nPixelSpace, nLineSpace, nBandSpace,
1643
0
                                        &sArg) == CE_None;
1644
0
                        GDALDestroyScaledProgress(sArg.pProgressData);
1645
0
                    }
1646
0
                    return bOK ? CE_None : CE_Failure;
1647
0
                }
1648
0
            }
1649
1650
0
            if (!ProcessRegion(nXOff, nYOff, nBufXSize, nBufYSize,
1651
0
                               psExtraArg->pfnProgress,
1652
0
                               psExtraArg->pProgressData))
1653
0
            {
1654
0
                return CE_Failure;
1655
0
            }
1656
0
            const auto eLastDT = m_aoSteps.back().eOutDT;
1657
0
            const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1658
0
            if (bIsBIPLike)
1659
0
            {
1660
0
                for (int iY = 0; iY < nBufYSize; ++iY)
1661
0
                {
1662
0
                    GDALCopyWords64(
1663
0
                        m_abyInput.data() + static_cast<size_t>(iY) * nBands *
1664
0
                                                nBufXSize * nLastDTSize,
1665
0
                        eLastDT, nLastDTSize, pabyData + iY * nLineSpace,
1666
0
                        eBufType, GDALGetDataTypeSizeBytes(eBufType),
1667
0
                        static_cast<size_t>(nBufXSize) * nBands);
1668
0
                }
1669
0
            }
1670
0
            else
1671
0
            {
1672
0
                CPLAssert(bIsBSQLike);
1673
0
                for (int iBand = 0; iBand < nBands; ++iBand)
1674
0
                {
1675
0
                    for (int iY = 0; iY < nBufYSize; ++iY)
1676
0
                    {
1677
0
                        GDALCopyWords64(
1678
0
                            m_abyInput.data() +
1679
0
                                (static_cast<size_t>(iY) * nBands * nBufXSize +
1680
0
                                 iBand) *
1681
0
                                    nLastDTSize,
1682
0
                            eLastDT, nLastDTSize * nBands,
1683
0
                            pabyData + iBand * nBandSpace + iY * nLineSpace,
1684
0
                            eBufType, nBufTypeSize, nBufXSize);
1685
0
                    }
1686
0
                }
1687
0
            }
1688
0
            return CE_None;
1689
0
        }
1690
0
    }
1691
1692
0
    return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1693
0
                                 nBufXSize, nBufYSize, eBufType, nBandCount,
1694
0
                                 panBandMap, nPixelSpace, nLineSpace,
1695
0
                                 nBandSpace, psExtraArg);
1696
0
}
1697
1698
/*! @endcond */
1699
1700
/************************************************************************/
1701
/*                GDALVRTRegisterProcessedDatasetFunc()                 */
1702
/************************************************************************/
1703
1704
/** Register a function to be used by VRTProcessedDataset.
1705
1706
 An example of content for pszXMLMetadata is:
1707
 \verbatim
1708
  <ProcessedDatasetFunctionArgumentsList>
1709
     <Argument name='src_nodata' type='double' description='Override input nodata value'/>
1710
     <Argument name='dst_nodata' type='double' description='Override output nodata value'/>
1711
     <Argument name='replacement_nodata' description='value to substitute to a valid computed value that would be nodata' type='double'/>
1712
     <Argument name='dst_intended_datatype' type='string' description='Intented datatype of output (which might be different than the working data type)'/>
1713
     <Argument name='coefficients_{band}' description='Comma-separated coefficients for combining bands. First one is constant term' type='double_list' required='true'/>
1714
  </ProcessedDatasetFunctionArgumentsList>
1715
 \endverbatim
1716
1717
 @param pszFuncName Function name. Must be unique and not null.
1718
 @param pUserData User data. May be nullptr. Must remain valid during the
1719
                  lifetime of GDAL.
1720
 @param pszXMLMetadata XML metadata describing the function arguments. May be
1721
                       nullptr if there are no arguments.
1722
 @param eRequestedInputDT If the pfnProcess callback only supports a single
1723
                          data type, it should be specified in this parameter.
1724
                          Otherwise set it to GDT_Unknown.
1725
 @param paeSupportedInputDT List of supported input data types. May be nullptr
1726
                            if all are supported or if eRequestedInputDT is
1727
                            set to a non GDT_Unknown value.
1728
 @param nSupportedInputDTSize Size of paeSupportedInputDT
1729
 @param panSupportedInputBandCount List of supported band count. May be nullptr
1730
                                   if any source band count is supported.
1731
 @param nSupportedInputBandCountSize Size of panSupportedInputBandCount
1732
 @param pfnInit Initialization function called when a VRTProcessedDataset
1733
                step uses the register function. This initialization function
1734
                will return the output data type, output band count and
1735
                potentially initialize a working structure, typically parsing
1736
                arguments. May be nullptr.
1737
                If not specified, it will be assumed that the input and output
1738
                data types are the same, and that the input number of bands
1739
                and output number of bands are the same.
1740
 @param pfnFree Free function that will free the working structure allocated
1741
                by pfnInit. May be nullptr.
1742
 @param pfnProcess Processing function called to compute pixel values. Must
1743
                   not be nullptr.
1744
 @param papszOptions Unused currently. Must be nullptr.
1745
 @return CE_None in case of success, error otherwise.
1746
 @since 3.9
1747
 */
1748
CPLErr GDALVRTRegisterProcessedDatasetFunc(
1749
    const char *pszFuncName, void *pUserData, const char *pszXMLMetadata,
1750
    GDALDataType eRequestedInputDT, const GDALDataType *paeSupportedInputDT,
1751
    size_t nSupportedInputDTSize, const int *panSupportedInputBandCount,
1752
    size_t nSupportedInputBandCountSize,
1753
    GDALVRTProcessedDatasetFuncInit pfnInit,
1754
    GDALVRTProcessedDatasetFuncFree pfnFree,
1755
    GDALVRTProcessedDatasetFuncProcess pfnProcess,
1756
    CPL_UNUSED CSLConstList papszOptions)
1757
0
{
1758
0
    if (pszFuncName == nullptr || pszFuncName[0] == '\0')
1759
0
    {
1760
0
        CPLError(CE_Failure, CPLE_AppDefined,
1761
0
                 "pszFuncName should be non-empty");
1762
0
        return CE_Failure;
1763
0
    }
1764
1765
0
    auto &oMap = GetGlobalMapProcessedDatasetFunc();
1766
0
    if (oMap.find(pszFuncName) != oMap.end())
1767
0
    {
1768
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
1769
0
                 pszFuncName);
1770
0
        return CE_Failure;
1771
0
    }
1772
1773
0
    if (!pfnProcess)
1774
0
    {
1775
0
        CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
1776
0
        return CE_Failure;
1777
0
    }
1778
1779
0
    VRTProcessedDatasetFunc oFunc;
1780
0
    oFunc.osFuncName = pszFuncName;
1781
0
    oFunc.pUserData = pUserData;
1782
0
    if (pszXMLMetadata)
1783
0
    {
1784
0
        oFunc.bMetadataSpecified = true;
1785
0
        auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
1786
0
        if (!psTree)
1787
0
        {
1788
0
            CPLError(CE_Failure, CPLE_AppDefined,
1789
0
                     "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
1790
0
                     pszFuncName);
1791
0
            return CE_Failure;
1792
0
        }
1793
0
        const CPLXMLNode *psRoot = CPLGetXMLNode(
1794
0
            psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
1795
0
        if (!psRoot)
1796
0
        {
1797
0
            CPLError(CE_Failure, CPLE_AppDefined,
1798
0
                     "No root ProcessedDatasetFunctionArgumentsList element in "
1799
0
                     "pszXMLMetadata=%s for %s",
1800
0
                     pszXMLMetadata, pszFuncName);
1801
0
            return CE_Failure;
1802
0
        }
1803
0
        for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
1804
0
             psIter = psIter->psNext)
1805
0
        {
1806
0
            if (psIter->eType == CXT_Element &&
1807
0
                strcmp(psIter->pszValue, "Argument") == 0)
1808
0
            {
1809
0
                const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
1810
0
                if (!pszName)
1811
0
                {
1812
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1813
0
                             "Missing Argument.name attribute in "
1814
0
                             "pszXMLMetadata=%s for %s",
1815
0
                             pszXMLMetadata, pszFuncName);
1816
0
                    return CE_Failure;
1817
0
                }
1818
0
                const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
1819
0
                if (!pszType)
1820
0
                {
1821
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1822
0
                             "Missing Argument.type attribute in "
1823
0
                             "pszXMLMetadata=%s for %s",
1824
0
                             pszXMLMetadata, pszFuncName);
1825
0
                    return CE_Failure;
1826
0
                }
1827
0
                if (strcmp(pszType, "constant") == 0)
1828
0
                {
1829
0
                    const char *pszValue =
1830
0
                        CPLGetXMLValue(psIter, "value", nullptr);
1831
0
                    if (!pszValue)
1832
0
                    {
1833
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1834
0
                                 "Missing Argument.value attribute in "
1835
0
                                 "pszXMLMetadata=%s for %s",
1836
0
                                 pszXMLMetadata, pszFuncName);
1837
0
                        return CE_Failure;
1838
0
                    }
1839
0
                    oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
1840
0
                        pszValue;
1841
0
                }
1842
0
                else if (strcmp(pszType, "builtin") == 0)
1843
0
                {
1844
0
                    if (EQUAL(pszName, "nodata") ||
1845
0
                        EQUAL(pszName, "offset_{band}") ||
1846
0
                        EQUAL(pszName, "scale_{band}"))
1847
0
                    {
1848
0
                        oFunc.oSetBuiltinArguments.insert(
1849
0
                            CPLString(pszName).tolower());
1850
0
                    }
1851
0
                    else
1852
0
                    {
1853
0
                        CPLError(CE_Failure, CPLE_NotSupported,
1854
0
                                 "Unsupported builtin parameter name %s in "
1855
0
                                 "pszXMLMetadata=%s for %s. Only nodata, "
1856
0
                                 "offset_{band} and scale_{band} are supported",
1857
0
                                 pszName, pszXMLMetadata, pszFuncName);
1858
0
                        return CE_Failure;
1859
0
                    }
1860
0
                }
1861
0
                else if (strcmp(pszType, "boolean") == 0 ||
1862
0
                         strcmp(pszType, "string") == 0 ||
1863
0
                         strcmp(pszType, "integer") == 0 ||
1864
0
                         strcmp(pszType, "double") == 0 ||
1865
0
                         strcmp(pszType, "double_list") == 0)
1866
0
                {
1867
0
                    VRTProcessedDatasetFunc::OtherArgument otherArgument;
1868
0
                    otherArgument.bRequired = CPLTestBool(
1869
0
                        CPLGetXMLValue(psIter, "required", "false"));
1870
0
                    otherArgument.osType = pszType;
1871
0
                    oFunc.oOtherArguments[CPLString(pszName).tolower()] =
1872
0
                        std::move(otherArgument);
1873
0
                }
1874
0
                else
1875
0
                {
1876
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1877
0
                             "Unsupported type for parameter %s in "
1878
0
                             "pszXMLMetadata=%s for %s. Only boolean, string, "
1879
0
                             "integer, double and double_list are supported",
1880
0
                             pszName, pszXMLMetadata, pszFuncName);
1881
0
                    return CE_Failure;
1882
0
                }
1883
0
            }
1884
0
        }
1885
0
    }
1886
0
    oFunc.eRequestedInputDT = eRequestedInputDT;
1887
0
    if (nSupportedInputDTSize)
1888
0
    {
1889
0
        oFunc.aeSupportedInputDT.insert(
1890
0
            oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
1891
0
            paeSupportedInputDT + nSupportedInputDTSize);
1892
0
    }
1893
0
    if (nSupportedInputBandCountSize)
1894
0
    {
1895
0
        oFunc.anSupportedInputBandCount.insert(
1896
0
            oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
1897
0
            panSupportedInputBandCount + nSupportedInputBandCountSize);
1898
0
    }
1899
0
    oFunc.pfnInit = pfnInit;
1900
0
    oFunc.pfnFree = pfnFree;
1901
0
    oFunc.pfnProcess = pfnProcess;
1902
1903
0
    oMap[pszFuncName] = std::move(oFunc);
1904
1905
0
    return CE_None;
1906
0
}