Coverage Report

Created: 2025-08-28 06:57

/src/gdal/gcore/gdalcomputedrasterband.cpp
Line
Count
Source (jump to first uncovered line)
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
19
/************************************************************************/
20
/*                        GDALComputedDataset                           */
21
/************************************************************************/
22
23
class GDALComputedDataset final : public GDALDataset
24
{
25
    friend class GDALComputedRasterBand;
26
27
    const GDALComputedRasterBand::Operation m_op;
28
    CPLStringList m_aosOptions{};
29
    std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
30
        m_bandDS{};
31
    std::vector<GDALRasterBand *> m_poBands{};
32
    VRTDataset m_oVRTDS;
33
34
    void AddSources(GDALComputedRasterBand *poBand);
35
36
    static const char *
37
    OperationToFunctionName(GDALComputedRasterBand::Operation op);
38
39
    GDALComputedDataset &operator=(const GDALComputedDataset &) = delete;
40
    GDALComputedDataset(GDALComputedDataset &&) = delete;
41
    GDALComputedDataset &operator=(GDALComputedDataset &&) = delete;
42
43
  public:
44
    GDALComputedDataset(const GDALComputedDataset &other);
45
46
    GDALComputedDataset(GDALComputedRasterBand *poBand, int nXSize, int nYSize,
47
                        GDALDataType eDT, int nBlockXSize, int nBlockYSize,
48
                        GDALComputedRasterBand::Operation op,
49
                        const GDALRasterBand *firstBand, double *pFirstConstant,
50
                        const GDALRasterBand *secondBand,
51
                        double *pSecondConstant);
52
53
    GDALComputedDataset(GDALComputedRasterBand *poBand, int nXSize, int nYSize,
54
                        GDALDataType eDT, int nBlockXSize, int nBlockYSize,
55
                        GDALComputedRasterBand::Operation op,
56
                        const std::vector<const GDALRasterBand *> &bands,
57
                        double constant);
58
59
    ~GDALComputedDataset() override;
60
61
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
62
0
    {
63
0
        return m_oVRTDS.GetGeoTransform(gt);
64
0
    }
65
66
    const OGRSpatialReference *GetSpatialRef() const override
67
0
    {
68
0
        return m_oVRTDS.GetSpatialRef();
69
0
    }
70
71
    char **GetMetadata(const char *pszDomain) override
72
0
    {
73
0
        return m_oVRTDS.GetMetadata(pszDomain);
74
0
    }
75
76
    const char *GetMetadataItem(const char *pszName,
77
                                const char *pszDomain) override
78
0
    {
79
0
        return m_oVRTDS.GetMetadataItem(pszName, pszDomain);
80
0
    }
81
82
    void *GetInternalHandle(const char *pszHandleName) override
83
0
    {
84
0
        if (pszHandleName && EQUAL(pszHandleName, "VRT_DATASET"))
85
0
            return &m_oVRTDS;
86
0
        return nullptr;
87
0
    }
88
};
89
90
/************************************************************************/
91
/*                        IsComparisonOperator()                        */
92
/************************************************************************/
93
94
static bool IsComparisonOperator(GDALComputedRasterBand::Operation op)
95
0
{
96
0
    switch (op)
97
0
    {
98
0
        case GDALComputedRasterBand::Operation::OP_GT:
99
0
        case GDALComputedRasterBand::Operation::OP_GE:
100
0
        case GDALComputedRasterBand::Operation::OP_LT:
101
0
        case GDALComputedRasterBand::Operation::OP_LE:
102
0
        case GDALComputedRasterBand::Operation::OP_EQ:
103
0
        case GDALComputedRasterBand::Operation::OP_NE:
104
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_AND:
105
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_OR:
106
0
            return true;
107
0
        default:
108
0
            break;
109
0
    }
110
0
    return false;
111
0
}
112
113
/************************************************************************/
114
/*                        GDALComputedDataset()                         */
115
/************************************************************************/
116
117
GDALComputedDataset::GDALComputedDataset(const GDALComputedDataset &other)
118
0
    : GDALDataset(), m_op(other.m_op), m_aosOptions(other.m_aosOptions),
119
0
      m_poBands(other.m_poBands),
120
0
      m_oVRTDS(other.GetRasterXSize(), other.GetRasterYSize(),
121
0
               other.m_oVRTDS.GetBlockXSize(), other.m_oVRTDS.GetBlockYSize())
122
0
{
123
0
    nRasterXSize = other.nRasterXSize;
124
0
    nRasterYSize = other.nRasterYSize;
125
126
0
    auto poBand = new GDALComputedRasterBand(
127
0
        const_cast<const GDALComputedRasterBand &>(
128
0
            *cpl::down_cast<GDALComputedRasterBand *>(
129
0
                const_cast<GDALComputedDataset &>(other).GetRasterBand(1))),
130
0
        true);
131
0
    SetBand(1, poBand);
132
133
0
    GDALGeoTransform gt;
134
0
    if (const_cast<VRTDataset &>(other.m_oVRTDS).GetGeoTransform(gt) == CE_None)
135
0
    {
136
0
        m_oVRTDS.SetGeoTransform(gt);
137
0
    }
138
139
0
    if (const auto *poSRS =
140
0
            const_cast<VRTDataset &>(other.m_oVRTDS).GetSpatialRef())
141
0
    {
142
0
        m_oVRTDS.SetSpatialRef(poSRS);
143
0
    }
144
145
0
    m_oVRTDS.AddBand(other.m_oVRTDS.GetRasterBand(1)->GetRasterDataType(),
146
0
                     m_aosOptions.List());
147
148
0
    AddSources(poBand);
149
0
}
150
151
/************************************************************************/
152
/*                        GDALComputedDataset()                         */
153
/************************************************************************/
154
155
GDALComputedDataset::GDALComputedDataset(
156
    GDALComputedRasterBand *poBand, int nXSize, int nYSize, GDALDataType eDT,
157
    int nBlockXSize, int nBlockYSize, GDALComputedRasterBand::Operation op,
158
    const GDALRasterBand *firstBand, double *pFirstConstant,
159
    const GDALRasterBand *secondBand, double *pSecondConstant)
160
0
    : m_op(op), m_oVRTDS(nXSize, nYSize, nBlockXSize, nBlockYSize)
161
0
{
162
0
    CPLAssert(firstBand != nullptr || secondBand != nullptr);
163
0
    if (firstBand)
164
0
        m_poBands.push_back(const_cast<GDALRasterBand *>(firstBand));
165
0
    if (secondBand)
166
0
        m_poBands.push_back(const_cast<GDALRasterBand *>(secondBand));
167
168
0
    nRasterXSize = nXSize;
169
0
    nRasterYSize = nYSize;
170
171
0
    if (auto poSrcDS = m_poBands.front()->GetDataset())
172
0
    {
173
0
        GDALGeoTransform gt;
174
0
        if (poSrcDS->GetGeoTransform(gt) == CE_None)
175
0
        {
176
0
            m_oVRTDS.SetGeoTransform(gt);
177
0
        }
178
179
0
        if (const auto *poSRS = poSrcDS->GetSpatialRef())
180
0
        {
181
0
            m_oVRTDS.SetSpatialRef(poSRS);
182
0
        }
183
0
    }
184
185
0
    if (op == GDALComputedRasterBand::Operation::OP_CAST)
186
0
    {
187
0
#ifdef DEBUG
188
        // Just for code coverage...
189
0
        CPL_IGNORE_RET_VAL(GDALComputedDataset::OperationToFunctionName(op));
190
0
#endif
191
0
        m_aosOptions.SetNameValue("subclass", "VRTSourcedRasterBand");
192
0
    }
193
0
    else
194
0
    {
195
0
        m_aosOptions.SetNameValue("subclass", "VRTDerivedRasterBand");
196
0
        if (IsComparisonOperator(op))
197
0
        {
198
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
199
0
            if (firstBand && secondBand)
200
0
            {
201
0
                m_aosOptions.SetNameValue(
202
0
                    "_PIXELFN_ARG_expression",
203
0
                    CPLSPrintf(
204
0
                        "source1 %s source2",
205
0
                        GDALComputedDataset::OperationToFunctionName(op)));
206
0
            }
207
0
            else if (firstBand && pSecondConstant)
208
0
            {
209
0
                m_aosOptions.SetNameValue(
210
0
                    "_PIXELFN_ARG_expression",
211
0
                    CPLSPrintf("source1 %s %.17g",
212
0
                               GDALComputedDataset::OperationToFunctionName(op),
213
0
                               *pSecondConstant));
214
0
            }
215
0
            else if (pFirstConstant && secondBand)
216
0
            {
217
0
                m_aosOptions.SetNameValue(
218
0
                    "_PIXELFN_ARG_expression",
219
0
                    CPLSPrintf(
220
0
                        "%.17g %s source1", *pFirstConstant,
221
0
                        GDALComputedDataset::OperationToFunctionName(op)));
222
0
            }
223
0
            else
224
0
            {
225
0
                CPLAssert(false);
226
0
            }
227
0
        }
228
0
        else if (op == GDALComputedRasterBand::Operation::OP_SUBTRACT &&
229
0
                 pSecondConstant)
230
0
        {
231
0
            m_aosOptions.SetNameValue("PixelFunctionType", "sum");
232
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
233
0
                                      CPLSPrintf("%.17g", -(*pSecondConstant)));
234
0
        }
235
0
        else if (op == GDALComputedRasterBand::Operation::OP_DIVIDE)
236
0
        {
237
0
            if (pSecondConstant)
238
0
            {
239
0
                m_aosOptions.SetNameValue("PixelFunctionType", "mul");
240
0
                m_aosOptions.SetNameValue(
241
0
                    "_PIXELFN_ARG_k",
242
0
                    CPLSPrintf("%.17g", 1.0 / (*pSecondConstant)));
243
0
            }
244
0
            else if (pFirstConstant)
245
0
            {
246
0
                m_aosOptions.SetNameValue("PixelFunctionType", "inv");
247
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
248
0
                                          CPLSPrintf("%.17g", *pFirstConstant));
249
0
            }
250
0
            else
251
0
            {
252
0
                m_aosOptions.SetNameValue("PixelFunctionType", "div");
253
0
            }
254
0
        }
255
0
        else if (op == GDALComputedRasterBand::Operation::OP_LOG)
256
0
        {
257
0
            CPLAssert(firstBand);
258
0
            CPLAssert(!secondBand);
259
0
            CPLAssert(!pFirstConstant);
260
0
            CPLAssert(!pSecondConstant);
261
0
            m_aosOptions.SetNameValue("PixelFunctionType", "expression");
262
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
263
0
                                      "log(source1)");
264
0
        }
265
0
        else if (op == GDALComputedRasterBand::Operation::OP_POW)
266
0
        {
267
0
            if (firstBand && secondBand)
268
0
            {
269
0
                m_aosOptions.SetNameValue("PixelFunctionType", "expression");
270
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
271
0
                                          "source1 ^ source2");
272
0
            }
273
0
            else if (firstBand && pSecondConstant)
274
0
            {
275
0
                m_aosOptions.SetNameValue("PixelFunctionType", "pow");
276
0
                m_aosOptions.SetNameValue(
277
0
                    "_PIXELFN_ARG_power",
278
0
                    CPLSPrintf("%.17g", *pSecondConstant));
279
0
            }
280
0
            else if (pFirstConstant && secondBand)
281
0
            {
282
0
                m_aosOptions.SetNameValue("PixelFunctionType", "exp");
283
0
                m_aosOptions.SetNameValue("_PIXELFN_ARG_base",
284
0
                                          CPLSPrintf("%.17g", *pFirstConstant));
285
0
            }
286
0
            else
287
0
            {
288
0
                CPLAssert(false);
289
0
            }
290
0
        }
291
0
        else
292
0
        {
293
0
            m_aosOptions.SetNameValue("PixelFunctionType",
294
0
                                      OperationToFunctionName(op));
295
0
            if (pSecondConstant)
296
0
                m_aosOptions.SetNameValue(
297
0
                    "_PIXELFN_ARG_k", CPLSPrintf("%.17g", *pSecondConstant));
298
0
        }
299
0
    }
300
0
    m_aosOptions.SetNameValue("_PIXELFN_ARG_propagateNoData", "true");
301
0
    m_oVRTDS.AddBand(eDT, m_aosOptions.List());
302
303
0
    SetBand(1, poBand);
304
305
0
    AddSources(poBand);
306
0
}
307
308
/************************************************************************/
309
/*                        GDALComputedDataset()                         */
310
/************************************************************************/
311
312
GDALComputedDataset::GDALComputedDataset(
313
    GDALComputedRasterBand *poBand, int nXSize, int nYSize, GDALDataType eDT,
314
    int nBlockXSize, int nBlockYSize, GDALComputedRasterBand::Operation op,
315
    const std::vector<const GDALRasterBand *> &bands, double constant)
316
0
    : m_op(op), m_oVRTDS(nXSize, nYSize, nBlockXSize, nBlockYSize)
317
0
{
318
0
    for (const GDALRasterBand *poIterBand : bands)
319
0
        m_poBands.push_back(const_cast<GDALRasterBand *>(poIterBand));
320
321
0
    nRasterXSize = nXSize;
322
0
    nRasterYSize = nYSize;
323
324
0
    if (auto poSrcDS = m_poBands.front()->GetDataset())
325
0
    {
326
0
        GDALGeoTransform gt;
327
0
        if (poSrcDS->GetGeoTransform(gt) == CE_None)
328
0
        {
329
0
            m_oVRTDS.SetGeoTransform(gt);
330
0
        }
331
332
0
        if (const auto *poSRS = poSrcDS->GetSpatialRef())
333
0
        {
334
0
            m_oVRTDS.SetSpatialRef(poSRS);
335
0
        }
336
0
    }
337
338
0
    m_aosOptions.SetNameValue("subclass", "VRTDerivedRasterBand");
339
0
    if (op == GDALComputedRasterBand::Operation::OP_TERNARY)
340
0
    {
341
0
        m_aosOptions.SetNameValue("PixelFunctionType", "expression");
342
0
        m_aosOptions.SetNameValue("_PIXELFN_ARG_expression",
343
0
                                  "source1 ? source2 : source3");
344
0
    }
345
0
    else
346
0
    {
347
0
        m_aosOptions.SetNameValue("PixelFunctionType",
348
0
                                  OperationToFunctionName(op));
349
0
        if (!std::isnan(constant))
350
0
        {
351
0
            m_aosOptions.SetNameValue("_PIXELFN_ARG_k",
352
0
                                      CPLSPrintf("%.17g", constant));
353
0
        }
354
0
        m_aosOptions.SetNameValue("_PIXELFN_ARG_propagateNoData", "true");
355
0
    }
356
0
    m_oVRTDS.AddBand(eDT, m_aosOptions.List());
357
358
0
    SetBand(1, poBand);
359
360
0
    AddSources(poBand);
361
0
}
362
363
/************************************************************************/
364
/*                       ~GDALComputedDataset()                         */
365
/************************************************************************/
366
367
0
GDALComputedDataset::~GDALComputedDataset() = default;
368
369
/************************************************************************/
370
/*                       HaveAllBandsSameNoDataValue()                  */
371
/************************************************************************/
372
373
static bool HaveAllBandsSameNoDataValue(GDALRasterBand **apoBands,
374
                                        size_t nBands, bool &hasAtLeastOneNDV,
375
                                        double &singleNDV)
376
0
{
377
0
    hasAtLeastOneNDV = false;
378
0
    singleNDV = 0;
379
380
0
    int bFirstBandHasNoData = false;
381
0
    for (size_t i = 0; i < nBands; ++i)
382
0
    {
383
0
        int bHasNoData = false;
384
0
        const double dfNoData = apoBands[i]->GetNoDataValue(&bHasNoData);
385
0
        if (bHasNoData)
386
0
            hasAtLeastOneNDV = true;
387
0
        if (i == 0)
388
0
        {
389
0
            bFirstBandHasNoData = bHasNoData;
390
0
            singleNDV = dfNoData;
391
0
        }
392
0
        else if (bHasNoData != bFirstBandHasNoData)
393
0
        {
394
0
            return false;
395
0
        }
396
0
        else if (bFirstBandHasNoData &&
397
0
                 !((std::isnan(singleNDV) && std::isnan(dfNoData)) ||
398
0
                   (singleNDV == dfNoData)))
399
0
        {
400
0
            return false;
401
0
        }
402
0
    }
403
0
    return true;
404
0
}
405
406
/************************************************************************/
407
/*                  GDALComputedDataset::AddSources()                   */
408
/************************************************************************/
409
410
void GDALComputedDataset::AddSources(GDALComputedRasterBand *poBand)
411
0
{
412
0
    auto poSourcedRasterBand =
413
0
        cpl::down_cast<VRTSourcedRasterBand *>(m_oVRTDS.GetRasterBand(1));
414
415
0
    bool hasAtLeastOneNDV = false;
416
0
    double singleNDV = 0;
417
0
    const bool bSameNDV = HaveAllBandsSameNoDataValue(
418
0
        m_poBands.data(), m_poBands.size(), hasAtLeastOneNDV, singleNDV);
419
420
    // For inputs that are instances of GDALComputedDataset, clone them
421
    // to make sure we do not depend on temporary instances,
422
    // such as "a + b + c", which is evaluated as "(a + b) + c", and the
423
    // temporary band/dataset corresponding to a + b will go out of scope
424
    // quickly.
425
0
    for (GDALRasterBand *&band : m_poBands)
426
0
    {
427
0
        auto poDS = band->GetDataset();
428
0
        if (auto poComputedDS = dynamic_cast<GDALComputedDataset *>(poDS))
429
0
        {
430
0
            auto poComputedDSNew =
431
0
                std::make_unique<GDALComputedDataset>(*poComputedDS);
432
0
            band = poComputedDSNew->GetRasterBand(1);
433
0
            m_bandDS.emplace_back(poComputedDSNew.release());
434
0
        }
435
436
0
        int bHasNoData = false;
437
0
        const double dfNoData = band->GetNoDataValue(&bHasNoData);
438
0
        if (bHasNoData)
439
0
        {
440
0
            poSourcedRasterBand->AddComplexSource(band, -1, -1, -1, -1, -1, -1,
441
0
                                                  -1, -1, 0, 1, dfNoData);
442
0
        }
443
0
        else
444
0
        {
445
0
            poSourcedRasterBand->AddSimpleSource(band);
446
0
        }
447
0
        poSourcedRasterBand->m_papoSources.back()->SetName(CPLSPrintf(
448
0
            "source%d",
449
0
            static_cast<int>(poSourcedRasterBand->m_papoSources.size())));
450
0
    }
451
0
    if (hasAtLeastOneNDV)
452
0
    {
453
0
        poBand->m_bHasNoData = true;
454
0
        if (bSameNDV)
455
0
        {
456
0
            poBand->m_dfNoDataValue = singleNDV;
457
0
        }
458
0
        else
459
0
        {
460
0
            poBand->m_dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
461
0
        }
462
0
        poSourcedRasterBand->SetNoDataValue(poBand->m_dfNoDataValue);
463
0
    }
464
0
}
465
466
/************************************************************************/
467
/*                       OperationToFunctionName()                      */
468
/************************************************************************/
469
470
/* static */ const char *GDALComputedDataset::OperationToFunctionName(
471
    GDALComputedRasterBand::Operation op)
472
0
{
473
0
    const char *ret = "";
474
0
    switch (op)
475
0
    {
476
0
        case GDALComputedRasterBand::Operation::OP_ADD:
477
0
            ret = "sum";
478
0
            break;
479
0
        case GDALComputedRasterBand::Operation::OP_SUBTRACT:
480
0
            ret = "diff";
481
0
            break;
482
0
        case GDALComputedRasterBand::Operation::OP_MULTIPLY:
483
0
            ret = "mul";
484
0
            break;
485
0
        case GDALComputedRasterBand::Operation::OP_DIVIDE:
486
0
            ret = "div";
487
0
            break;
488
0
        case GDALComputedRasterBand::Operation::OP_MIN:
489
0
            ret = "min";
490
0
            break;
491
0
        case GDALComputedRasterBand::Operation::OP_MAX:
492
0
            ret = "max";
493
0
            break;
494
0
        case GDALComputedRasterBand::Operation::OP_MEAN:
495
0
            ret = "mean";
496
0
            break;
497
0
        case GDALComputedRasterBand::Operation::OP_GT:
498
0
            ret = ">";
499
0
            break;
500
0
        case GDALComputedRasterBand::Operation::OP_GE:
501
0
            ret = ">=";
502
0
            break;
503
0
        case GDALComputedRasterBand::Operation::OP_LT:
504
0
            ret = "<";
505
0
            break;
506
0
        case GDALComputedRasterBand::Operation::OP_LE:
507
0
            ret = "<=";
508
0
            break;
509
0
        case GDALComputedRasterBand::Operation::OP_EQ:
510
0
            ret = "==";
511
0
            break;
512
0
        case GDALComputedRasterBand::Operation::OP_NE:
513
0
            ret = "!=";
514
0
            break;
515
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_AND:
516
0
            ret = "&&";
517
0
            break;
518
0
        case GDALComputedRasterBand::Operation::OP_LOGICAL_OR:
519
0
            ret = "||";
520
0
            break;
521
0
        case GDALComputedRasterBand::Operation::OP_CAST:
522
0
        case GDALComputedRasterBand::Operation::OP_TERNARY:
523
0
            break;
524
0
        case GDALComputedRasterBand::Operation::OP_ABS:
525
0
            ret = "mod";
526
0
            break;
527
0
        case GDALComputedRasterBand::Operation::OP_SQRT:
528
0
            ret = "sqrt";
529
0
            break;
530
0
        case GDALComputedRasterBand::Operation::OP_LOG:
531
0
            ret = "log";
532
0
            break;
533
0
        case GDALComputedRasterBand::Operation::OP_LOG10:
534
0
            ret = "log10";
535
0
            break;
536
0
        case GDALComputedRasterBand::Operation::OP_POW:
537
0
            ret = "pow";
538
0
            break;
539
0
    }
540
0
    return ret;
541
0
}
542
543
/************************************************************************/
544
/*                       GDALComputedRasterBand()                       */
545
/************************************************************************/
546
547
GDALComputedRasterBand::GDALComputedRasterBand(
548
    const GDALComputedRasterBand &other, bool)
549
0
    : GDALRasterBand()
550
0
{
551
0
    nRasterXSize = other.nRasterXSize;
552
0
    nRasterYSize = other.nRasterYSize;
553
0
    eDataType = other.eDataType;
554
0
    nBlockXSize = other.nBlockXSize;
555
0
    nBlockYSize = other.nBlockYSize;
556
0
}
557
558
//! @cond Doxygen_Suppress
559
560
/************************************************************************/
561
/*                       GDALComputedRasterBand()                       */
562
/************************************************************************/
563
564
GDALComputedRasterBand::GDALComputedRasterBand(
565
    Operation op, const std::vector<const GDALRasterBand *> &bands,
566
    double constant)
567
0
{
568
0
    CPLAssert(op == Operation::OP_ADD || op == Operation::OP_MIN ||
569
0
              op == Operation::OP_MAX || op == Operation::OP_MEAN ||
570
0
              op == Operation::OP_TERNARY);
571
572
0
    CPLAssert(!bands.empty());
573
0
    nRasterXSize = bands[0]->GetXSize();
574
0
    nRasterYSize = bands[0]->GetYSize();
575
0
    eDataType = bands[0]->GetRasterDataType();
576
0
    for (size_t i = 1; i < bands.size(); ++i)
577
0
    {
578
0
        eDataType = GDALDataTypeUnion(eDataType, bands[i]->GetRasterDataType());
579
0
    }
580
581
0
    bool hasAtLeastOneNDV = false;
582
0
    double singleNDV = 0;
583
0
    const bool bSameNDV =
584
0
        HaveAllBandsSameNoDataValue(const_cast<GDALRasterBand **>(bands.data()),
585
0
                                    bands.size(), hasAtLeastOneNDV, singleNDV);
586
587
0
    if (!bSameNDV)
588
0
    {
589
0
        eDataType = eDataType == GDT_Float64 ? GDT_Float64 : GDT_Float32;
590
0
    }
591
0
    else if (op == Operation::OP_TERNARY)
592
0
    {
593
0
        CPLAssert(bands.size() == 3);
594
0
        eDataType = GDALDataTypeUnion(bands[1]->GetRasterDataType(),
595
0
                                      bands[2]->GetRasterDataType());
596
0
    }
597
0
    else if (!std::isnan(constant) && eDataType != GDT_Float64)
598
0
    {
599
0
        if (op == Operation::OP_MIN || op == Operation::OP_MAX)
600
0
        {
601
0
            eDataType = GDALDataTypeUnionWithValue(eDataType, constant, false);
602
0
        }
603
0
        else
604
0
        {
605
0
            eDataType = (static_cast<float>(constant) == constant)
606
0
                            ? GDT_Float32
607
0
                            : GDT_Float64;
608
0
        }
609
0
    }
610
0
    bands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
611
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
612
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
613
0
        op, bands, constant);
614
0
    m_poOwningDS.reset(l_poDS.release());
615
0
}
616
617
/************************************************************************/
618
/*                       GDALComputedRasterBand()                       */
619
/************************************************************************/
620
621
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
622
                                               const GDALRasterBand &firstBand,
623
                                               const GDALRasterBand &secondBand)
624
0
{
625
0
    nRasterXSize = firstBand.GetXSize();
626
0
    nRasterYSize = firstBand.GetYSize();
627
628
0
    bool hasAtLeastOneNDV = false;
629
0
    double singleNDV = 0;
630
0
    GDALRasterBand *apoBands[] = {const_cast<GDALRasterBand *>(&firstBand),
631
0
                                  const_cast<GDALRasterBand *>(&secondBand)};
632
0
    const bool bSameNDV =
633
0
        HaveAllBandsSameNoDataValue(apoBands, 2, hasAtLeastOneNDV, singleNDV);
634
635
0
    const auto firstDT = firstBand.GetRasterDataType();
636
0
    const auto secondDT = secondBand.GetRasterDataType();
637
0
    if (!bSameNDV)
638
0
        eDataType = (firstDT == GDT_Float64 || secondDT == GDT_Float64)
639
0
                        ? GDT_Float64
640
0
                        : GDT_Float32;
641
0
    else if (IsComparisonOperator(op))
642
0
        eDataType = GDT_Byte;
643
0
    else if (op == Operation::OP_ADD && firstDT == GDT_Byte &&
644
0
             secondDT == GDT_Byte)
645
0
        eDataType = GDT_UInt16;
646
0
    else if (firstDT == GDT_Float32 && secondDT == GDT_Float32)
647
0
        eDataType = GDT_Float32;
648
0
    else if ((op == Operation::OP_MIN || op == Operation::OP_MAX) &&
649
0
             firstDT == secondDT)
650
0
        eDataType = firstDT;
651
0
    else
652
0
        eDataType = GDT_Float64;
653
0
    firstBand.GetBlockSize(&nBlockXSize, &nBlockYSize);
654
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
655
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
656
0
        op, &firstBand, nullptr, &secondBand, nullptr);
657
0
    m_poOwningDS.reset(l_poDS.release());
658
0
}
659
660
/************************************************************************/
661
/*                       GDALComputedRasterBand()                       */
662
/************************************************************************/
663
664
GDALComputedRasterBand::GDALComputedRasterBand(Operation op, double constant,
665
                                               const GDALRasterBand &band)
666
0
{
667
0
    CPLAssert(op == Operation::OP_DIVIDE || IsComparisonOperator(op) ||
668
0
              op == Operation::OP_POW);
669
670
0
    nRasterXSize = band.GetXSize();
671
0
    nRasterYSize = band.GetYSize();
672
0
    const auto firstDT = band.GetRasterDataType();
673
0
    if (IsComparisonOperator(op))
674
0
        eDataType = GDT_Byte;
675
0
    else if (firstDT == GDT_Float32 && static_cast<float>(constant) == constant)
676
0
        eDataType = GDT_Float32;
677
0
    else
678
0
        eDataType = GDT_Float64;
679
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
680
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
681
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
682
0
        op, nullptr, &constant, &band, nullptr);
683
0
    m_poOwningDS.reset(l_poDS.release());
684
0
}
685
686
/************************************************************************/
687
/*                       GDALComputedRasterBand()                       */
688
/************************************************************************/
689
690
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
691
                                               const GDALRasterBand &band,
692
                                               double constant)
693
0
{
694
0
    nRasterXSize = band.GetXSize();
695
0
    nRasterYSize = band.GetYSize();
696
0
    const auto firstDT = band.GetRasterDataType();
697
0
    if (IsComparisonOperator(op))
698
0
        eDataType = GDT_Byte;
699
0
    else if (op == Operation::OP_ADD && firstDT == GDT_Byte &&
700
0
             constant >= -128 && constant <= 127 &&
701
0
             std::floor(constant) == constant)
702
0
        eDataType = GDT_Byte;
703
0
    else if (firstDT == GDT_Float32 && static_cast<float>(constant) == constant)
704
0
        eDataType = GDT_Float32;
705
0
    else
706
0
        eDataType = GDT_Float64;
707
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
708
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
709
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
710
0
        op, &band, nullptr, nullptr, &constant);
711
0
    m_poOwningDS.reset(l_poDS.release());
712
0
}
713
714
/************************************************************************/
715
/*                       GDALComputedRasterBand()                       */
716
/************************************************************************/
717
718
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
719
                                               const GDALRasterBand &band)
720
0
{
721
0
    CPLAssert(op == Operation::OP_ABS || op == Operation::OP_SQRT ||
722
0
              op == Operation::OP_LOG || op == Operation::OP_LOG10);
723
0
    nRasterXSize = band.GetXSize();
724
0
    nRasterYSize = band.GetYSize();
725
0
    eDataType =
726
0
        band.GetRasterDataType() == GDT_Float32 ? GDT_Float32 : GDT_Float64;
727
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
728
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
729
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
730
0
        op, &band, nullptr, nullptr, nullptr);
731
0
    m_poOwningDS.reset(l_poDS.release());
732
0
}
733
734
/************************************************************************/
735
/*                       GDALComputedRasterBand()                       */
736
/************************************************************************/
737
738
GDALComputedRasterBand::GDALComputedRasterBand(Operation op,
739
                                               const GDALRasterBand &band,
740
                                               GDALDataType dt)
741
0
{
742
0
    CPLAssert(op == Operation::OP_CAST);
743
0
    nRasterXSize = band.GetXSize();
744
0
    nRasterYSize = band.GetYSize();
745
0
    eDataType = dt;
746
0
    band.GetBlockSize(&nBlockXSize, &nBlockYSize);
747
0
    auto l_poDS = std::make_unique<GDALComputedDataset>(
748
0
        this, nRasterXSize, nRasterYSize, eDataType, nBlockXSize, nBlockYSize,
749
0
        op, &band, nullptr, nullptr, nullptr);
750
0
    m_poOwningDS.reset(l_poDS.release());
751
0
}
752
753
//! @endcond
754
755
/************************************************************************/
756
/*                      ~GDALComputedRasterBand()                       */
757
/************************************************************************/
758
759
GDALComputedRasterBand::~GDALComputedRasterBand()
760
0
{
761
0
    if (m_poOwningDS)
762
0
        cpl::down_cast<GDALComputedDataset *>(m_poOwningDS.get())->nBands = 0;
763
0
    poDS = nullptr;
764
0
}
765
766
/************************************************************************/
767
/*                  GDALComputedRasterBand::GetNoDataValue()            */
768
/************************************************************************/
769
770
double GDALComputedRasterBand::GetNoDataValue(int *pbHasNoData)
771
0
{
772
0
    if (pbHasNoData)
773
0
        *pbHasNoData = m_bHasNoData;
774
0
    return m_dfNoDataValue;
775
0
}
776
777
/************************************************************************/
778
/*                    GDALComputedRasterBandRelease()                   */
779
/************************************************************************/
780
781
/** Release a GDALComputedRasterBandH
782
 *
783
 * @since 3.12
784
 */
785
void GDALComputedRasterBandRelease(GDALComputedRasterBandH hBand)
786
0
{
787
0
    delete GDALComputedRasterBand::FromHandle(hBand);
788
0
}
789
790
/************************************************************************/
791
/*                           IReadBlock()                               */
792
/************************************************************************/
793
794
CPLErr GDALComputedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
795
                                          void *pData)
796
0
{
797
0
    auto l_poDS = cpl::down_cast<GDALComputedDataset *>(poDS);
798
0
    return l_poDS->m_oVRTDS.GetRasterBand(1)->ReadBlock(nBlockXOff, nBlockYOff,
799
0
                                                        pData);
800
0
}
801
802
/************************************************************************/
803
/*                           IRasterIO()                                */
804
/************************************************************************/
805
806
CPLErr GDALComputedRasterBand::IRasterIO(
807
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
808
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
809
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
810
0
{
811
0
    auto l_poDS = cpl::down_cast<GDALComputedDataset *>(poDS);
812
0
    return l_poDS->m_oVRTDS.GetRasterBand(1)->RasterIO(
813
0
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
814
0
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
815
0
}