Coverage Report

Created: 2025-11-16 06:25

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