Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdalcomputedrasterband.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  GDALComputedDataset and GDALComputedRasterBand
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdal_priv.h"
14
#include "vrtdataset.h"
15
16
#include <cmath>
17
#include <limits>
18
#include <set>
19
#include <utility>
20
21
/************************************************************************/
22
/*                         GDALComputedDataset                          */
23
/************************************************************************/
24
25
class GDALComputedDataset final : public GDALDataset
26
{
27
    friend class GDALComputedRasterBand;
28
29
    std::string m_expr{};
30
    CPLStringList m_aosOptions{};
31
    std::vector<std::pair<std::string, GDALRasterBand *>> m_apoBands{};
32
    VRTDataset m_oVRTDS;
33
34
    std::pair<bool, std::string> AddSourceBand(const GDALRasterBand *band);
35
36
    void AddSources(GDALComputedRasterBand *poBand);
37
38
    static const char *
39
    OperationToFunctionName(GDALComputedRasterBand::Operation op,
40
                            bool bForceMuparser = false);
41
42
    GDALComputedDataset &operator=(const GDALComputedDataset &) = delete;
43
    GDALComputedDataset(GDALComputedDataset &&) = delete;
44
    GDALComputedDataset &operator=(GDALComputedDataset &&) = delete;
45
46
  public:
47
    GDALComputedDataset(GDALComputedRasterBand *poBand, int nXSize, int nYSize,
48
                        GDALDataType eDT, int nBlockXSize, int nBlockYSize,
49
                        GDALComputedRasterBand::Operation op,
50
                        const GDALRasterBand *firstBand, double *pFirstConstant,
51
                        const GDALRasterBand *secondBand,
52
                        double *pSecondConstant);
53
54
    GDALComputedDataset(GDALComputedRasterBand *poBand, int nXSize, int nYSize,
55
                        GDALDataType eDT, int nBlockXSize, int nBlockYSize,
56
                        GDALComputedRasterBand::Operation op,
57
                        const std::vector<const GDALRasterBand *> &bands,
58
                        double constant);
59
60
    ~GDALComputedDataset() override;
61
62
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
63
0
    {
64
0
        return m_oVRTDS.GetGeoTransform(gt);
65
0
    }
66
67
    const OGRSpatialReference *GetSpatialRef() const override
68
0
    {
69
0
        return m_oVRTDS.GetSpatialRef();
70
0
    }
71
72
    CSLConstList GetMetadata(const char *pszDomain) override
73
0
    {
74
0
        return m_oVRTDS.GetMetadata(pszDomain);
75
0
    }
76
77
    const char *GetMetadataItem(const char *pszName,
78
                                const char *pszDomain) override
79
0
    {
80
0
        return m_oVRTDS.GetMetadataItem(pszName, pszDomain);
81
0
    }
82
83
    void *GetInternalHandle(const char *pszHandleName) override
84
0
    {
85
0
        if (pszHandleName && EQUAL(pszHandleName, "VRT_DATASET"))
86
0
            return &m_oVRTDS;
87
0
        return nullptr;
88
0
    }
89
};
90
91
/************************************************************************/
92
/*                        IsComparisonOperator()                        */
93
/************************************************************************/
94
95
static bool IsComparisonOperator(GDALComputedRasterBand::Operation op)
96
0
{
97
0
    switch (op)
98
0
    {
99
0
        case GDALComputedRasterBand::Operation::OP_GT:
100
0
        case GDALComputedRasterBand::Operation::OP_GE:
101
0
        case GDALComputedRasterBand::Operation::OP_LT:
102
0
        case GDALComputedRasterBand::Operation::OP_LE:
103
0
        case GDALComputedRasterBand::Operation::OP_EQ:
104
0
        case GDALComputedRasterBand::Operation::OP_NE:
105
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_AND:
106
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_OR:
107
0
            return true;
108
0
        default:
109
0
            break;
110
0
    }
111
0
    return false;
112
0
}
113
114
/************************************************************************/
115
/*                           AddSourceBand()                            */
116
/************************************************************************/
117
118
/** Returns a pair (bIsExprBand, osBandName) */
119
std::pair<bool, std::string>
120
GDALComputedDataset::AddSourceBand(const GDALRasterBand *band)
121
0
{
122
0
    auto bandDS = band->GetDataset();
123
0
    if (auto poComputedDS = dynamic_cast<GDALComputedDataset *>(bandDS))
124
0
    {
125
0
        m_apoBands.insert(m_apoBands.end(), poComputedDS->m_apoBands.begin(),
126
0
                          poComputedDS->m_apoBands.end());
127
0
        return {true, poComputedDS->m_expr};
128
0
    }
129
0
    else
130
0
    {
131
0
        std::string osName = CPLSPrintf("band_%p", band);
132
0
        m_apoBands.emplace_back(osName, const_cast<GDALRasterBand *>(band));
133
0
        return {false, osName};
134
0
    }
135
0
}
136
137
/************************************************************************/
138
/*                        GDALComputedDataset()                         */
139
/************************************************************************/
140
141
GDALComputedDataset::GDALComputedDataset(
142
    GDALComputedRasterBand *poBand, int nXSize, int nYSize, GDALDataType eDT,
143
    int nBlockXSize, int nBlockYSize, GDALComputedRasterBand::Operation op,
144
    const GDALRasterBand *firstBand, double *pFirstConstant,
145
    const GDALRasterBand *secondBand, double *pSecondConstant)
146
0
    : m_oVRTDS(nXSize, nYSize, nBlockXSize, nBlockYSize)
147
0
{
148
0
    CPLAssert(firstBand != nullptr || secondBand != nullptr);
149
0
    std::string osFirstBand;
150
0
    bool bCanUseBuiltin = true;
151
0
    if (firstBand)
152
0
    {
153
0
        const auto [bIsExprBand, name] = AddSourceBand(firstBand);
154
0
        bCanUseBuiltin = bCanUseBuiltin && !bIsExprBand;
155
0
        if (bIsExprBand)
156
0
            osFirstBand = "(";
157
0
        osFirstBand += name;
158
0
        if (bIsExprBand)
159
0
            osFirstBand += ')';
160
0
    }
161
0
    std::string osSecondBand;
162
0
    if (secondBand)
163
0
    {
164
0
        const auto [bIsExprBand, name] = AddSourceBand(secondBand);
165
0
        bCanUseBuiltin = bCanUseBuiltin && !bIsExprBand;
166
0
        if (bIsExprBand)
167
0
            osSecondBand = "(";
168
0
        osSecondBand += name;
169
0
        if (bIsExprBand)
170
0
            osSecondBand += ')';
171
0
    }
172
173
0
    nRasterXSize = nXSize;
174
0
    nRasterYSize = nYSize;
175
176
0
    if (auto poSrcDS = m_apoBands.front().second->GetDataset())
177
0
    {
178
0
        GDALGeoTransform gt;
179
0
        if (poSrcDS->GetGeoTransform(gt) == CE_None)
180
0
        {
181
0
            m_oVRTDS.SetGeoTransform(gt);
182
0
        }
183
184
0
        if (const auto *poSRS = poSrcDS->GetSpatialRef())
185
0
        {
186
0
            m_oVRTDS.SetSpatialRef(poSRS);
187
0
        }
188
0
    }
189
190
0
    if (op == GDALComputedRasterBand::Operation::OP_CAST)
191
0
    {
192
0
#ifdef DEBUG
193
        // Just for code coverage...
194
0
        CPL_IGNORE_RET_VAL(GDALComputedDataset::OperationToFunctionName(op));
195
0
#endif
196
197
0
        m_expr = osFirstBand;
198
0
        if (m_apoBands.size() > 1)
199
0
        {
200
0
            m_aosOptions.SetNameValue("subclass", "VRTDerivedRasterBand");
201
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
202
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
203
0
                                      m_expr.c_str());
204
0
        }
205
0
        else
206
0
        {
207
0
            m_aosOptions.SetNameValue("subclass", "VRTSourcedRasterBand");
208
0
        }
209
0
    }
210
0
    else
211
0
    {
212
0
        m_aosOptions.SetNameValue("subclass", "VRTDerivedRasterBand");
213
0
        if (IsComparisonOperator(op))
214
0
        {
215
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
216
0
            if (firstBand && secondBand)
217
0
            {
218
0
                m_expr = osFirstBand;
219
0
                m_expr += ' ';
220
0
                m_expr += GDALComputedDataset::OperationToFunctionName(op);
221
0
                m_expr += ' ';
222
0
                m_expr += osSecondBand;
223
0
            }
224
0
            else if (firstBand && pSecondConstant)
225
0
            {
226
0
                m_expr = osFirstBand;
227
0
                m_expr += ' ';
228
0
                m_expr += GDALComputedDataset::OperationToFunctionName(op);
229
0
                m_expr += ' ';
230
0
                m_expr += CPLSPrintf("%.17g", *pSecondConstant);
231
0
            }
232
0
            else if (pFirstConstant && secondBand)
233
0
            {
234
0
                m_expr = CPLSPrintf("%.17g", *pFirstConstant);
235
0
                m_expr += ' ';
236
0
                m_expr += GDALComputedDataset::OperationToFunctionName(op);
237
0
                m_expr += ' ';
238
0
                m_expr += osSecondBand;
239
0
            }
240
0
            else
241
0
            {
242
0
                CPLAssert(false);
243
0
            }
244
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
245
0
                                      m_expr.c_str());
246
0
        }
247
0
        else if (op == GDALComputedRasterBand::Operation::OP_SUBTRACT &&
248
0
                 pSecondConstant)
249
0
        {
250
0
            m_aosOptions.SetNameValue("PixelFunctionType", "sum");
251
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
252
0
                                      CPLSPrintf("%.17g", -(*pSecondConstant)));
253
0
            m_expr = osFirstBand;
254
0
            m_expr += CPLSPrintf(" - %.17g", *pSecondConstant);
255
0
        }
256
0
        else if (op == GDALComputedRasterBand::Operation::OP_DIVIDE)
257
0
        {
258
0
            if (pSecondConstant)
259
0
            {
260
0
                m_aosOptions.SetNameValue("PixelFunctionType", "mul");
261
0
                m_aosOptions.SetNameValue(
262
0
                    "_PIXELFN_ARG_k",
263
0
                    CPLSPrintf("%.17g", 1.0 / (*pSecondConstant)));
264
0
                m_expr = osFirstBand;
265
0
                m_expr += CPLSPrintf(" * %.17g", 1.0 / (*pSecondConstant));
266
0
            }
267
0
            else if (pFirstConstant)
268
0
            {
269
0
                m_aosOptions.SetNameValue("PixelFunctionType", "inv");
270
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
271
0
                                          CPLSPrintf("%.17g", *pFirstConstant));
272
0
                m_expr = CPLSPrintf("%.17g / ", *pFirstConstant);
273
0
                m_expr += osSecondBand;
274
0
            }
275
0
            else
276
0
            {
277
0
                m_aosOptions.SetNameValue("PixelFunctionType", "div");
278
0
                m_expr = osFirstBand;
279
0
                m_expr += " / ";
280
0
                m_expr += osSecondBand;
281
0
            }
282
0
        }
283
0
        else if (op == GDALComputedRasterBand::Operation::OP_LOG)
284
0
        {
285
0
            CPLAssert(firstBand);
286
0
            CPLAssert(!secondBand);
287
0
            CPLAssert(!pFirstConstant);
288
0
            CPLAssert(!pSecondConstant);
289
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
290
0
            m_expr = "log(";
291
0
            m_expr += osFirstBand;
292
0
            m_expr += ')';
293
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
294
0
                                      m_expr.c_str());
295
0
        }
296
0
        else if (op == GDALComputedRasterBand::Operation::OP_POW)
297
0
        {
298
0
            if (firstBand && secondBand)
299
0
            {
300
0
                m_aosOptions.SetNameValue("PixelFunctionType", "expression");
301
0
                m_expr = osFirstBand;
302
0
                m_expr += " ^ ";
303
0
                m_expr += osSecondBand;
304
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
305
0
                                          m_expr.c_str());
306
0
            }
307
0
            else if (firstBand && pSecondConstant)
308
0
            {
309
0
                m_aosOptions.SetNameValue("PixelFunctionType", "pow");
310
0
                m_aosOptions.SetNameValue(
311
0
                    "_PIXELFN_ARG_power",
312
0
                    CPLSPrintf("%.17g", *pSecondConstant));
313
0
                m_expr = osFirstBand;
314
0
                m_expr += " ^ ";
315
0
                m_expr += CPLSPrintf("%.17g", *pSecondConstant);
316
0
            }
317
0
            else if (pFirstConstant && secondBand)
318
0
            {
319
0
                m_aosOptions.SetNameValue("PixelFunctionType", "exp");
320
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_base",
321
0
                                          CPLSPrintf("%.17g", *pFirstConstant));
322
0
                m_expr = CPLSPrintf("%.17g", *pFirstConstant);
323
0
                m_expr += " ^ ";
324
0
                m_expr += osSecondBand;
325
0
            }
326
0
            else
327
0
            {
328
0
                CPLAssert(false);
329
0
            }
330
0
        }
331
0
        else
332
0
        {
333
0
            if (firstBand && secondBand)
334
0
            {
335
0
                if (op == GDALComputedRasterBand::Operation::OP_MIN ||
336
0
                    op == GDALComputedRasterBand::Operation::OP_MAX ||
337
0
                    op == GDALComputedRasterBand::Operation::OP_MEAN)
338
0
                {
339
0
                    m_expr +=
340
0
                        GDALComputedDataset::OperationToFunctionName(op, true);
341
0
                    m_expr += '(';
342
0
                    m_expr += osFirstBand;
343
0
                    m_expr += ',';
344
0
                    m_expr += osSecondBand;
345
0
                    m_expr += ')';
346
0
                }
347
0
                else
348
0
                {
349
0
                    m_expr = osFirstBand;
350
0
                    m_expr += ' ';
351
0
                    m_expr +=
352
0
                        GDALComputedDataset::OperationToFunctionName(op, true);
353
0
                    m_expr += ' ';
354
0
                    m_expr += osSecondBand;
355
0
                }
356
0
            }
357
0
            else if (firstBand && pSecondConstant)
358
0
            {
359
0
                m_expr = osFirstBand;
360
0
                m_expr += ' ';
361
0
                m_expr +=
362
0
                    GDALComputedDataset::OperationToFunctionName(op, true);
363
0
                m_expr += ' ';
364
0
                m_expr += CPLSPrintf("%.17g", *pSecondConstant);
365
0
            }
366
0
            else if (pFirstConstant && secondBand)
367
0
            {
368
0
                m_expr = CPLSPrintf("%.17g", *pFirstConstant);
369
0
                m_expr += ' ';
370
0
                m_expr +=
371
0
                    GDALComputedDataset::OperationToFunctionName(op, true);
372
0
                m_expr += ' ';
373
0
                m_expr += osSecondBand;
374
0
            }
375
0
            else
376
0
            {
377
0
                m_expr = GDALComputedDataset::OperationToFunctionName(op, true);
378
0
                m_expr += '(';
379
0
                m_expr += osFirstBand;
380
0
                m_expr += ')';
381
0
            }
382
383
0
            if (bCanUseBuiltin)
384
0
            {
385
0
                m_aosOptions.SetNameValue("PixelFunctionType",
386
0
                                          OperationToFunctionName(op));
387
0
                if (pSecondConstant)
388
0
                {
389
0
                    m_aosOptions.SetNameValue(
390
0
                        "_PIXELFN_ARG_k",
391
0
                        CPLSPrintf("%.17g", *pSecondConstant));
392
0
                }
393
0
            }
394
0
            else
395
0
            {
396
0
                m_aosOptions.SetNameValue("PixelFunctionType", "expression");
397
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
398
0
                                          m_expr.c_str());
399
0
            }
400
0
        }
401
0
    }
402
0
    m_aosOptions.SetNameValue("_PIXELFN_ARG_propagateNoData", "true");
403
0
    m_oVRTDS.AddBand(eDT, m_aosOptions.List());
404
405
0
    SetBand(1, poBand);
406
407
0
    AddSources(poBand);
408
409
    // Otherwise coverity scan suggets adding annoying std::move()s
410
0
    CPL_IGNORE_RET_VAL(osFirstBand);
411
0
    CPL_IGNORE_RET_VAL(osSecondBand);
412
0
}
413
414
/************************************************************************/
415
/*                        GDALComputedDataset()                         */
416
/************************************************************************/
417
418
GDALComputedDataset::GDALComputedDataset(
419
    GDALComputedRasterBand *poBand, int nXSize, int nYSize, GDALDataType eDT,
420
    int nBlockXSize, int nBlockYSize, GDALComputedRasterBand::Operation op,
421
    const std::vector<const GDALRasterBand *> &bands, double constant)
422
0
    : m_oVRTDS(nXSize, nYSize, nBlockXSize, nBlockYSize)
423
0
{
424
0
    bool bCanUseBuiltin = true;
425
0
    std::vector<std::string> aosNames;
426
0
    for (const GDALRasterBand *poIterBand : bands)
427
0
    {
428
0
        const auto [bIsExprBand, name] = AddSourceBand(poIterBand);
429
0
        bCanUseBuiltin = bCanUseBuiltin && !bIsExprBand;
430
0
        if (!bIsExprBand)
431
0
            aosNames.push_back(std::move(name));
432
0
        else
433
0
            aosNames.push_back(std::string("(").append(name).append(")"));
434
0
    }
435
436
0
    nRasterXSize = nXSize;
437
0
    nRasterYSize = nYSize;
438
439
0
    if (auto poSrcDS = m_apoBands.front().second->GetDataset())
440
0
    {
441
0
        GDALGeoTransform gt;
442
0
        if (poSrcDS->GetGeoTransform(gt) == CE_None)
443
0
        {
444
0
            m_oVRTDS.SetGeoTransform(gt);
445
0
        }
446
447
0
        if (const auto *poSRS = poSrcDS->GetSpatialRef())
448
0
        {
449
0
            m_oVRTDS.SetSpatialRef(poSRS);
450
0
        }
451
0
    }
452
453
0
    m_aosOptions.SetNameValue("subclass", "VRTDerivedRasterBand");
454
0
    if (op == GDALComputedRasterBand::Operation::OP_TERNARY)
455
0
    {
456
0
        m_expr = aosNames[0];
457
0
        m_expr += " ? ";
458
0
        m_expr += aosNames[1];
459
0
        m_expr += " : ";
460
0
        m_expr += aosNames[2];
461
462
0
        m_aosOptions.SetNameValue("PixelFunctionType", "expression");
463
0
        m_aosOptions.SetNameValue("_PIXELFN_ARG_expression", m_expr.c_str());
464
0
    }
465
0
    else
466
0
    {
467
0
        m_expr = OperationToFunctionName(op);
468
0
        m_expr += '(';
469
0
        bool first = true;
470
0
        for (const auto &name : aosNames)
471
0
        {
472
0
            if (!first)
473
0
                m_expr += ", ";
474
0
            m_expr += name;
475
0
            first = false;
476
0
        }
477
0
        if (!std::isnan(constant))
478
0
        {
479
0
            if (!first)
480
0
                m_expr += ", ";
481
0
            m_expr += CPLSPrintf("%.17g", constant);
482
0
        }
483
0
        m_expr += ')';
484
485
0
        if (bCanUseBuiltin)
486
0
        {
487
0
            m_aosOptions.SetNameValue("PixelFunctionType",
488
0
                                      OperationToFunctionName(op));
489
0
            if (!std::isnan(constant))
490
0
            {
491
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
492
0
                                          CPLSPrintf("%.17g", constant));
493
0
            }
494
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_propagateNoData", "true");
495
0
        }
496
0
        else
497
0
        {
498
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
499
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
500
0
                                      m_expr.c_str());
501
0
        }
502
0
    }
503
0
    m_oVRTDS.AddBand(eDT, m_aosOptions.List());
504
505
0
    SetBand(1, poBand);
506
507
0
    AddSources(poBand);
508
0
}
509
510
/************************************************************************/
511
/*                        ~GDALComputedDataset()                        */
512
/************************************************************************/
513
514
0
GDALComputedDataset::~GDALComputedDataset() = default;
515
516
/************************************************************************/
517
/*                    HaveAllBandsSameNoDataValue()                     */
518
/************************************************************************/
519
520
static bool HaveAllBandsSameNoDataValue(GDALRasterBand **apoBands,
521
                                        size_t nBands, bool &hasAtLeastOneNDV,
522
                                        double &singleNDV)
523
0
{
524
0
    hasAtLeastOneNDV = false;
525
0
    singleNDV = 0;
526
527
0
    int bFirstBandHasNoData = false;
528
0
    for (size_t i = 0; i < nBands; ++i)
529
0
    {
530
0
        int bHasNoData = false;
531
0
        const double dfNoData = apoBands[i]->GetNoDataValue(&bHasNoData);
532
0
        if (bHasNoData)
533
0
            hasAtLeastOneNDV = true;
534
0
        if (i == 0)
535
0
        {
536
0
            bFirstBandHasNoData = bHasNoData;
537
0
            singleNDV = dfNoData;
538
0
        }
539
0
        else if (bHasNoData != bFirstBandHasNoData)
540
0
        {
541
0
            return false;
542
0
        }
543
0
        else if (bFirstBandHasNoData &&
544
0
                 !((std::isnan(singleNDV) && std::isnan(dfNoData)) ||
545
0
                   (singleNDV == dfNoData)))
546
0
        {
547
0
            return false;
548
0
        }
549
0
    }
550
0
    return true;
551
0
}
552
553
/************************************************************************/
554
/*                  GDALComputedDataset::AddSources()                   */
555
/************************************************************************/
556
557
void GDALComputedDataset::AddSources(GDALComputedRasterBand *poBand)
558
0
{
559
0
    auto poSourcedRasterBand =
560
0
        cpl::down_cast<VRTSourcedRasterBand *>(m_oVRTDS.GetRasterBand(1));
561
562
0
    bool hasAtLeastOneNDV = false;
563
0
    double singleNDV = 0;
564
0
    std::vector<GDALRasterBand *> apoSrcBands;
565
0
    for (auto &[_, band] : m_apoBands)
566
0
    {
567
0
        apoSrcBands.push_back(band);
568
0
    }
569
0
    const bool bSameNDV = HaveAllBandsSameNoDataValue(
570
0
        apoSrcBands.data(), m_apoBands.size(), hasAtLeastOneNDV, singleNDV);
571
572
0
    std::set<std::string> alreadyAdded;
573
0
    for (auto &[name, band] : m_apoBands)
574
0
    {
575
0
        if (alreadyAdded.insert(name).second)
576
0
        {
577
0
            int bHasNoData = false;
578
0
            const double dfNoData = band->GetNoDataValue(&bHasNoData);
579
0
            if (bHasNoData)
580
0
            {
581
0
                poSourcedRasterBand->AddComplexSource(
582
0
                    band, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, dfNoData);
583
0
            }
584
0
            else
585
0
            {
586
0
                poSourcedRasterBand->AddSimpleSource(band);
587
0
            }
588
0
            poSourcedRasterBand->m_papoSources.back()->SetName(name.c_str());
589
0
        }
590
0
    }
591
0
    if (hasAtLeastOneNDV)
592
0
    {
593
0
        poBand->m_bHasNoData = true;
594
0
        if (bSameNDV)
595
0
        {
596
0
            poBand->m_dfNoDataValue = singleNDV;
597
0
        }
598
0
        else
599
0
        {
600
0
            poBand->m_dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
601
0
        }
602
0
        poSourcedRasterBand->SetNoDataValue(poBand->m_dfNoDataValue);
603
0
    }
604
0
}
605
606
/************************************************************************/
607
/*                      OperationToFunctionName()                       */
608
/************************************************************************/
609
610
/* static */ const char *GDALComputedDataset::OperationToFunctionName(
611
    GDALComputedRasterBand::Operation op, bool bForceMuparser)
612
0
{
613
0
    const char *ret = "";
614
0
    switch (op)
615
0
    {
616
0
        case GDALComputedRasterBand::Operation::OP_ADD:
617
0
            ret = bForceMuparser ? "+" : "sum";
618
0
            break;
619
0
        case GDALComputedRasterBand::Operation::OP_SUBTRACT:
620
0
            ret = bForceMuparser ? "-" : "diff";
621
0
            break;
622
0
        case GDALComputedRasterBand::Operation::OP_MULTIPLY:
623
0
            ret = bForceMuparser ? "*" : "mul";
624
0
            break;
625
0
        case GDALComputedRasterBand::Operation::OP_DIVIDE:
626
0
            ret = bForceMuparser ? "/" : "div";
627
0
            break;
628
0
        case GDALComputedRasterBand::Operation::OP_MIN:
629
0
            ret = "min";
630
0
            break;
631
0
        case GDALComputedRasterBand::Operation::OP_MAX:
632
0
            ret = "max";
633
0
            break;
634
0
        case GDALComputedRasterBand::Operation::OP_MEAN:
635
0
            ret = "mean";
636
0
            break;
637
0
        case GDALComputedRasterBand::Operation::OP_GT:
638
0
            ret = ">";
639
0
            break;
640
0
        case GDALComputedRasterBand::Operation::OP_GE:
641
0
            ret = ">=";
642
0
            break;
643
0
        case GDALComputedRasterBand::Operation::OP_LT:
644
0
            ret = "<";
645
0
            break;
646
0
        case GDALComputedRasterBand::Operation::OP_LE:
647
0
            ret = "<=";
648
0
            break;
649
0
        case GDALComputedRasterBand::Operation::OP_EQ:
650
0
            ret = "==";
651
0
            break;
652
0
        case GDALComputedRasterBand::Operation::OP_NE:
653
0
            ret = "!=";
654
0
            break;
655
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_AND:
656
0
            ret = "&&";
657
0
            break;
658
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_OR:
659
0
            ret = "||";
660
0
            break;
661
0
        case GDALComputedRasterBand::Operation::OP_CAST:
662
0
        case GDALComputedRasterBand::Operation::OP_TERNARY:
663
0
            break;
664
0
        case GDALComputedRasterBand::Operation::OP_ABS:
665
0
            ret = bForceMuparser ? "abs" : "mod";
666
0
            break;
667
0
        case GDALComputedRasterBand::Operation::OP_SQRT:
668
0
            ret = "sqrt";
669
0
            break;
670
0
        case GDALComputedRasterBand::Operation::OP_LOG:
671
0
            ret = "log";
672
0
            break;
673
0
        case GDALComputedRasterBand::Operation::OP_LOG10:
674
0
            ret = "log10";
675
0
            break;
676
0
        case GDALComputedRasterBand::Operation::OP_POW:
677
0
            ret = "pow";
678
0
            break;
679
0
    }
680
0
    return ret;
681
0
}
682
683
/************************************************************************/
684
/*                       GDALComputedRasterBand()                       */
685
/************************************************************************/
686
687
GDALComputedRasterBand::GDALComputedRasterBand(
688
    const GDALComputedRasterBand &other, bool)
689
0
    : GDALRasterBand()
690
0
{
691
0
    nRasterXSize = other.nRasterXSize;
692
0
    nRasterYSize = other.nRasterYSize;
693
0
    eDataType = other.eDataType;
694
0
    nBlockXSize = other.nBlockXSize;
695
0
    nBlockYSize = other.nBlockYSize;
696
0
}
697
698
//! @cond Doxygen_Suppress
699
700
/************************************************************************/
701
/*                       GDALComputedRasterBand()                       */
702
/************************************************************************/
703
704
GDALComputedRasterBand::GDALComputedRasterBand(
705
    Operation op, const std::vector<const GDALRasterBand *> &bands,
706
    double constant)
707
0
{
708
0
    CPLAssert(op == Operation::OP_ADD || op == Operation::OP_MIN ||
709
0
              op == Operation::OP_MAX || op == Operation::OP_MEAN ||
710
0
              op == Operation::OP_TERNARY);
711
712
0
    CPLAssert(!bands.empty());
713
0
    nRasterXSize = bands[0]->GetXSize();
714
0
    nRasterYSize = bands[0]->GetYSize();
715
0
    eDataType = bands[0]->GetRasterDataType();
716
0
    for (size_t i = 1; i < bands.size(); ++i)
717
0
    {
718
0
        eDataType = GDALDataTypeUnion(eDataType, bands[i]->GetRasterDataType());
719
0
    }
720
721
0
    bool hasAtLeastOneNDV = false;
722
0
    double singleNDV = 0;
723
0
    const bool bSameNDV =
724
0
        HaveAllBandsSameNoDataValue(const_cast<GDALRasterBand **>(bands.data()),
725
0
                                    bands.size(), hasAtLeastOneNDV, singleNDV);
726
727
0
    if (!bSameNDV)
728
0
    {
729
0
        eDataType = eDataType == GDT_Float64 ? GDT_Float64 : GDT_Float32;
730
0
    }
731
0
    else if (op == Operation::OP_TERNARY)
732
0
    {
733
0
        CPLAssert(bands.size() == 3);
734
0
        eDataType = GDALDataTypeUnion(bands[1]->GetRasterDataType(),
735
0
                                      bands[2]->GetRasterDataType());
736
0
    }
737
0
    else if (!std::isnan(constant) && eDataType != GDT_Float64)
738
0
    {
739
0
        if (op == Operation::OP_MIN || op == Operation::OP_MAX)
740
0
        {
741
0
            eDataType = GDALDataTypeUnionWithValue(eDataType, constant, false);
742
0
        }
743
0
        else
744
0
        {
745
0
            eDataType =
746
0
                (static_cast<double>(static_cast<float>(constant)) == constant)
747
0
                    ? GDT_Float32
748
0
                    : GDT_Float64;
749
0
        }
750
0
    }
751
0
    bands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
752
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
753
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
754
0
        op, bands, constant);
755
0
    m_poOwningDS.reset(l_poDS.release());
756
0
}
757
758
/************************************************************************/
759
/*                       GDALComputedRasterBand()                       */
760
/************************************************************************/
761
762
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
763
                                               const GDALRasterBand &firstBand,
764
                                               const GDALRasterBand &secondBand)
765
0
{
766
0
    nRasterXSize = firstBand.GetXSize();
767
0
    nRasterYSize = firstBand.GetYSize();
768
769
0
    bool hasAtLeastOneNDV = false;
770
0
    double singleNDV = 0;
771
0
    GDALRasterBand *apoBands[] = {const_cast<GDALRasterBand *>(&firstBand),
772
0
                                  const_cast<GDALRasterBand *>(&secondBand)};
773
0
    const bool bSameNDV =
774
0
        HaveAllBandsSameNoDataValue(apoBands, 2, hasAtLeastOneNDV, singleNDV);
775
776
0
    const auto firstDT = firstBand.GetRasterDataType();
777
0
    const auto secondDT = secondBand.GetRasterDataType();
778
0
    if (!bSameNDV)
779
0
        eDataType = (firstDT == GDT_Float64 || secondDT == GDT_Float64)
780
0
                        ? GDT_Float64
781
0
                        : GDT_Float32;
782
0
    else if (IsComparisonOperator(op))
783
0
        eDataType = GDT_UInt8;
784
0
    else if (op == Operation::OP_ADD && firstDT == GDT_UInt8 &&
785
0
             secondDT == GDT_UInt8)
786
0
        eDataType = GDT_UInt16;
787
0
    else if (firstDT == GDT_Float32 && secondDT == GDT_Float32)
788
0
        eDataType = GDT_Float32;
789
0
    else if ((op == Operation::OP_MIN || op == Operation::OP_MAX) &&
790
0
             firstDT == secondDT)
791
0
        eDataType = firstDT;
792
0
    else
793
0
        eDataType = GDT_Float64;
794
0
    firstBand.GetBlockSize(&nBlockXSize, &nBlockYSize);
795
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
796
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
797
0
        op, &firstBand, nullptr, &secondBand, nullptr);
798
0
    m_poOwningDS.reset(l_poDS.release());
799
0
}
800
801
/************************************************************************/
802
/*                       GDALComputedRasterBand()                       */
803
/************************************************************************/
804
805
GDALComputedRasterBand::GDALComputedRasterBand(Operation op, double constant,
806
                                               const GDALRasterBand &band)
807
0
{
808
0
    CPLAssert(op == Operation::OP_DIVIDE || IsComparisonOperator(op) ||
809
0
              op == Operation::OP_POW);
810
811
0
    nRasterXSize = band.GetXSize();
812
0
    nRasterYSize = band.GetYSize();
813
0
    const auto firstDT = band.GetRasterDataType();
814
0
    if (IsComparisonOperator(op))
815
0
        eDataType = GDT_UInt8;
816
0
    else if (firstDT == GDT_Float32 &&
817
0
             static_cast<double>(static_cast<float>(constant)) == constant)
818
0
        eDataType = GDT_Float32;
819
0
    else
820
0
        eDataType = GDT_Float64;
821
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
822
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
823
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
824
0
        op, nullptr, &constant, &band, nullptr);
825
0
    m_poOwningDS.reset(l_poDS.release());
826
0
}
827
828
/************************************************************************/
829
/*                       GDALComputedRasterBand()                       */
830
/************************************************************************/
831
832
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
833
                                               const GDALRasterBand &band,
834
                                               double constant)
835
0
{
836
0
    nRasterXSize = band.GetXSize();
837
0
    nRasterYSize = band.GetYSize();
838
0
    const auto firstDT = band.GetRasterDataType();
839
0
    if (IsComparisonOperator(op))
840
0
        eDataType = GDT_UInt8;
841
0
    else if (op == Operation::OP_ADD && firstDT == GDT_UInt8 &&
842
0
             constant >= -128 && constant <= 127 &&
843
0
             std::floor(constant) == constant)
844
0
        eDataType = GDT_UInt8;
845
0
    else if (firstDT == GDT_Float32 &&
846
0
             static_cast<double>(static_cast<float>(constant)) == constant)
847
0
        eDataType = GDT_Float32;
848
0
    else
849
0
        eDataType = GDT_Float64;
850
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
851
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
852
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
853
0
        op, &band, nullptr, nullptr, &constant);
854
0
    m_poOwningDS.reset(l_poDS.release());
855
0
}
856
857
/************************************************************************/
858
/*                       GDALComputedRasterBand()                       */
859
/************************************************************************/
860
861
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
862
                                               const GDALRasterBand &band)
863
0
{
864
0
    CPLAssert(op == Operation::OP_ABS || op == Operation::OP_SQRT ||
865
0
              op == Operation::OP_LOG || op == Operation::OP_LOG10);
866
0
    nRasterXSize = band.GetXSize();
867
0
    nRasterYSize = band.GetYSize();
868
0
    eDataType =
869
0
        band.GetRasterDataType() == GDT_Float32 ? GDT_Float32 : GDT_Float64;
870
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
871
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
872
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
873
0
        op, &band, nullptr, nullptr, nullptr);
874
0
    m_poOwningDS.reset(l_poDS.release());
875
0
}
876
877
/************************************************************************/
878
/*                       GDALComputedRasterBand()                       */
879
/************************************************************************/
880
881
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
882
                                               const GDALRasterBand &band,
883
                                               GDALDataType dt)
884
0
{
885
0
    CPLAssert(op == Operation::OP_CAST);
886
0
    nRasterXSize = band.GetXSize();
887
0
    nRasterYSize = band.GetYSize();
888
0
    eDataType = dt;
889
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
890
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
891
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
892
0
        op, &band, nullptr, nullptr, nullptr);
893
0
    m_poOwningDS.reset(l_poDS.release());
894
0
}
895
896
//! @endcond
897
898
/************************************************************************/
899
/*                      ~GDALComputedRasterBand()                       */
900
/************************************************************************/
901
902
GDALComputedRasterBand::~GDALComputedRasterBand()
903
0
{
904
0
    if (m_poOwningDS)
905
0
        cpl::down_cast<GDALComputedDataset *>(m_poOwningDS.get())->nBands = 0;
906
0
    poDS = nullptr;
907
0
}
908
909
/************************************************************************/
910
/*               GDALComputedRasterBand::GetNoDataValue()               */
911
/************************************************************************/
912
913
double GDALComputedRasterBand::GetNoDataValue(int *pbHasNoData)
914
0
{
915
0
    if (pbHasNoData)
916
0
        *pbHasNoData = m_bHasNoData;
917
0
    return m_dfNoDataValue;
918
0
}
919
920
/************************************************************************/
921
/*                   GDALComputedRasterBandRelease()                    */
922
/************************************************************************/
923
924
/** Release a GDALComputedRasterBandH
925
 *
926
 * @since 3.12
927
 */
928
void GDALComputedRasterBandRelease(GDALComputedRasterBandH hBand)
929
0
{
930
0
    delete GDALComputedRasterBand::FromHandle(hBand);
931
0
}
932
933
/************************************************************************/
934
/*                             IReadBlock()                             */
935
/************************************************************************/
936
937
CPLErr GDALComputedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
938
                                          void *pData)
939
0
{
940
0
    auto l_poDS = cpl::down_cast<GDALComputedDataset *>(poDS);
941
0
    return l_poDS->m_oVRTDS.GetRasterBand(1)->ReadBlock(nBlockXOff, nBlockYOff,
942
0
                                                        pData);
943
0
}
944
945
/************************************************************************/
946
/*                             IRasterIO()                              */
947
/************************************************************************/
948
949
CPLErr GDALComputedRasterBand::IRasterIO(
950
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
951
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
952
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
953
0
{
954
0
    auto l_poDS = cpl::down_cast<GDALComputedDataset *>(poDS);
955
0
    return l_poDS->m_oVRTDS.GetRasterBand(1)->RasterIO(
956
0
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
957
0
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
958
0
}