• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.0
/frmts/tiledb/tiledbdense.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL TileDB Driver
4
 * Purpose:  Implement GDAL TileDB Support based on https://www.tiledb.io
5
 * Author:   TileDB, Inc
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2019, TileDB, Inc
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include <cassert>
14
#include <cmath>
15
#include <cinttypes>
16
#include <limits>
17

18
#include "gdal_priv_templates.hpp"
19
#include "tiledbheaders.h"
20

21
// XML element inside _gdal XML metadata to store the number of overview levels
22
constexpr const char *OVERVIEW_COUNT_KEY = "tiledb:OverviewCount";
23

24
/************************************************************************/
25
/* ==================================================================== */
26
/*                            TileDBRasterBand                          */
27
/* ==================================================================== */
28
/************************************************************************/
29

30
class TileDBRasterBand final : public GDALPamRasterBand
31
{
32
    friend class TileDBRasterDataset;
33

34
  protected:
35
    TileDBRasterDataset *poGDS;
36
    bool bStats;
37
    CPLString osAttrName;
38
    double m_dfNoData = 0;
39
    bool m_bNoDataSet = false;
40

41
    CPL_DISALLOW_COPY_ASSIGN(TileDBRasterBand)
42

43
  public:
44
    TileDBRasterBand(TileDBRasterDataset *, int,
45
                     const std::string &osAttr = TILEDB_VALUES);
46
    virtual CPLErr IReadBlock(int, int, void *) override;
47
    virtual CPLErr IWriteBlock(int, int, void *) override;
48
    virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
49
                             GDALDataType, GSpacing, GSpacing,
50
                             GDALRasterIOExtraArg *psExtraArg) override;
51
    virtual GDALColorInterp GetColorInterpretation() override;
52

53
    double GetNoDataValue(int *pbHasNoData) override;
54
    CPLErr SetNoDataValue(double dfNoData) override;
55

56
    int GetOverviewCount() override;
57
    GDALRasterBand *GetOverview(int nIdx) override;
58
};
59

60
static CPLErr option_to_index_type(const char *pszIndexingType,
226✔
61
                                   TILEDB_INTERLEAVE_MODE &eMode)
62
{
63
    CPLErr eErr = CE_None;
226✔
64

65
    if (pszIndexingType)
226✔
66
    {
67
        if EQUAL (pszIndexingType, "BAND")
139✔
68
            eMode = BAND;
108✔
69
        else if EQUAL (pszIndexingType, "ATTRIBUTES")
31✔
70
            eMode = ATTRIBUTES;
8✔
71
        else if EQUAL (pszIndexingType, "PIXEL")
23✔
72
            eMode = PIXEL;
23✔
73
        else
74
        {
75
            eErr = CE_Failure;
×
76
            CPLError(eErr, CPLE_AppDefined,
×
77
                     "Unable to identify TileDB index mode %s.",
78
                     pszIndexingType);
79
        }
80
    }
81
    else
82
    {
83
        eMode = BAND;
87✔
84
    }
85

86
    return eErr;
226✔
87
}
88

89
static const char *index_type_name(TILEDB_INTERLEAVE_MODE eMode)
106✔
90
{
91
    switch (eMode)
106✔
92
    {
93
        case PIXEL:
8✔
94
            return "PIXEL";
8✔
95
        case ATTRIBUTES:
5✔
96
            return "ATTRIBUTES";
5✔
97
        case BAND:
93✔
98
            return "BAND";
93✔
99
        default:
×
100
            return nullptr;
×
101
    }
102
}
103

104
/************************************************************************/
105
/*                             SetBuffer()                              */
106
/************************************************************************/
107

108
static CPLErr SetBuffer(tiledb::Query *poQuery, GDALDataType eType,
402✔
109
                        const CPLString &osAttrName, void *pImage, size_t nSize)
110
{
111
    switch (eType)
402✔
112
    {
113
        case GDT_Byte:
200✔
114
            poQuery->set_data_buffer(
115
                osAttrName, reinterpret_cast<unsigned char *>(pImage), nSize);
200✔
116
            break;
200✔
117
        case GDT_Int8:
2✔
118
            poQuery->set_data_buffer(osAttrName,
119
                                     reinterpret_cast<int8_t *>(pImage), nSize);
2✔
120
            break;
2✔
121
        case GDT_UInt16:
3✔
122
            poQuery->set_data_buffer(
123
                osAttrName, reinterpret_cast<unsigned short *>(pImage), nSize);
3✔
124
            break;
3✔
125
        case GDT_UInt32:
3✔
126
            poQuery->set_data_buffer(
127
                osAttrName, reinterpret_cast<unsigned int *>(pImage), nSize);
3✔
128
            break;
3✔
129
        case GDT_UInt64:
2✔
130
            poQuery->set_data_buffer(
131
                osAttrName, reinterpret_cast<uint64_t *>(pImage), nSize);
2✔
132
            break;
2✔
133
        case GDT_Int16:
3✔
134
            poQuery->set_data_buffer(osAttrName,
135
                                     reinterpret_cast<short *>(pImage), nSize);
3✔
136
            break;
3✔
137
        case GDT_Int32:
9✔
138
            poQuery->set_data_buffer(osAttrName,
139
                                     reinterpret_cast<int *>(pImage), nSize);
9✔
140
            break;
9✔
141
        case GDT_Int64:
2✔
142
            poQuery->set_data_buffer(
143
                osAttrName, reinterpret_cast<int64_t *>(pImage), nSize);
2✔
144
            break;
2✔
145
        case GDT_Float32:
159✔
146
            poQuery->set_data_buffer(osAttrName,
147
                                     reinterpret_cast<float *>(pImage), nSize);
159✔
148
            break;
159✔
149
        case GDT_Float64:
3✔
150
            poQuery->set_data_buffer(osAttrName,
151
                                     reinterpret_cast<double *>(pImage), nSize);
3✔
152
            break;
3✔
153
        case GDT_CInt16:
3✔
154
            poQuery->set_data_buffer(
155
                osAttrName, reinterpret_cast<short *>(pImage), nSize * 2);
3✔
156
            break;
3✔
157
        case GDT_CInt32:
3✔
158
            poQuery->set_data_buffer(
159
                osAttrName, reinterpret_cast<int *>(pImage), nSize * 2);
3✔
160
            break;
3✔
161
        case GDT_CFloat32:
3✔
162
            poQuery->set_data_buffer(
163
                osAttrName, reinterpret_cast<float *>(pImage), nSize * 2);
3✔
164
            break;
3✔
165
        case GDT_CFloat64:
7✔
166
            poQuery->set_data_buffer(
167
                osAttrName, reinterpret_cast<double *>(pImage), nSize * 2);
7✔
168
            break;
7✔
169
        default:
×
170
            return CE_Failure;
×
171
    }
172
    return CE_None;
402✔
173
}
174

175
/************************************************************************/
176
/*                          TileDBRasterBand()                          */
177
/************************************************************************/
178

179
TileDBRasterBand::TileDBRasterBand(TileDBRasterDataset *poDSIn, int nBandIn,
928✔
180
                                   const std::string &osAttr)
928✔
181
    : poGDS(poDSIn), bStats(poDSIn->bStats), osAttrName(osAttr)
928✔
182
{
183
    poDS = poDSIn;
928✔
184
    nBand = nBandIn;
928✔
185
    eDataType = poGDS->eDataType;
928✔
186
    if (eDataType == GDT_Unknown)
928✔
187
    {
188
        try
189
        {
190
            auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
14✔
191
                            ->schema()
14✔
192
                            .attribute(osAttr);
28✔
193
            switch (attr.type())
14✔
194
            {
195
                case TILEDB_INT8:
1✔
196
                    eDataType = GDT_Int8;
1✔
197
                    break;
1✔
198
                case TILEDB_UINT8:
1✔
199
                    eDataType = GDT_Byte;
1✔
200
                    break;
1✔
201
                case TILEDB_INT16:
2✔
202
                    eDataType =
2✔
203
                        attr.cell_val_num() == 2 ? GDT_CInt16 : GDT_Int16;
2✔
204
                    break;
2✔
205
                case TILEDB_UINT16:
1✔
206
                    eDataType = GDT_UInt16;
1✔
207
                    break;
1✔
208
                case TILEDB_INT32:
2✔
209
                    eDataType =
2✔
210
                        attr.cell_val_num() == 2 ? GDT_CInt32 : GDT_Int32;
2✔
211
                    break;
2✔
212
                case TILEDB_UINT32:
1✔
213
                    eDataType = GDT_UInt32;
1✔
214
                    break;
1✔
215
                case TILEDB_INT64:
1✔
216
                    eDataType = GDT_Int64;
1✔
217
                    break;
1✔
218
                case TILEDB_UINT64:
1✔
219
                    eDataType = GDT_UInt64;
1✔
220
                    break;
1✔
221
                case TILEDB_FLOAT32:
2✔
222
                    eDataType =
2✔
223
                        attr.cell_val_num() == 2 ? GDT_CFloat32 : GDT_Float32;
2✔
224
                    break;
2✔
225
                case TILEDB_FLOAT64:
2✔
226
                    eDataType =
2✔
227
                        attr.cell_val_num() == 2 ? GDT_CFloat64 : GDT_Float64;
2✔
228
                    break;
2✔
229
                default:
×
230
                {
231
                    const char *pszTypeName = "";
×
232
                    tiledb_datatype_to_str(attr.type(), &pszTypeName);
×
233
                    CPLError(CE_Failure, CPLE_NotSupported,
×
234
                             "Unhandled TileDB data type: %s", pszTypeName);
235
                    break;
×
236
                }
237
            }
238
        }
239
        catch (const tiledb::TileDBError &e)
×
240
        {
241
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
242
        }
243
    }
244
    eAccess = poGDS->eAccess;
928✔
245
    nRasterXSize = poGDS->nRasterXSize;
928✔
246
    nRasterYSize = poGDS->nRasterYSize;
928✔
247
    nBlockXSize = poGDS->nBlockXSize;
928✔
248
    nBlockYSize = poGDS->nBlockYSize;
928✔
249
}
928✔
250

251
/************************************************************************/
252
/*                             IRasterIO()                              */
253
/************************************************************************/
254

255
CPLErr TileDBRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
344✔
256
                                   int nXSize, int nYSize, void *pData,
257
                                   int nBufXSize, int nBufYSize,
258
                                   GDALDataType eBufType, GSpacing nPixelSpace,
259
                                   GSpacing nLineSpace,
260
                                   GDALRasterIOExtraArg *psExtraArg)
261
{
262
    if (!poGDS->m_bDeferredCreateHasRun)
344✔
263
        poGDS->DeferredCreate(/* bCreateArray = */ true);
5✔
264
    if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
344✔
265
        return CE_Failure;
×
266
    if (!poGDS->m_array)
344✔
267
    {
268
        CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
1✔
269
        return CE_Failure;
1✔
270
    }
271

272
    if (poGDS->eIndexMode == ATTRIBUTES && eRWFlag == GF_Write)
343✔
273
    {
274
        CPLError(CE_Failure, CPLE_NoWriteAccess,
×
275
                 "Unable to write using band ordered IRasterIO when using "
276
                 "interleave 'ATTRIBUTES'.\n");
277
        return CE_Failure;
×
278
    }
279

280
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
343✔
281

282
    if (eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
343✔
283
        nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
280✔
284
        nLineSpace == nBufXSize * nPixelSpace)
280✔
285
    {
286
        const uint64_t nBandIdx = poGDS->nBandStart + nBand - 1;
280✔
287
        std::vector<uint64_t> oaSubarray = {
288
            nBandIdx,
289
            nBandIdx,
290
            static_cast<uint64_t>(nYOff),
280✔
291
            static_cast<uint64_t>(nYOff) + nYSize - 1,
280✔
292
            static_cast<uint64_t>(nXOff),
280✔
293
            static_cast<uint64_t>(nXOff) + nXSize - 1};
560✔
294
        if (poGDS->eIndexMode == PIXEL)
280✔
295
            std::rotate(oaSubarray.begin(), oaSubarray.begin() + 2,
84✔
296
                        oaSubarray.end());
84✔
297

298
        try
299
        {
300
            tiledb::Context *ctx = poGDS->m_ctx.get();
280✔
301
            const auto &oArray = poGDS->GetArray(eRWFlag == GF_Write, ctx);
280✔
302

303
            auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
560✔
304
            tiledb::Subarray subarray(*ctx, oArray);
560✔
305
            if (poGDS->m_array->schema().domain().ndim() == 3)
280✔
306
            {
307
                subarray.set_subarray(oaSubarray);
213✔
308
            }
309
            else
310
            {
311
                subarray.set_subarray(std::vector<uint64_t>(
268✔
312
                    oaSubarray.cbegin() + 2, oaSubarray.cend()));
201✔
313
            }
314
            poQuery->set_subarray(subarray);
280✔
315

316
            const size_t nValues = static_cast<size_t>(nBufXSize) * nBufYSize;
280✔
317
            SetBuffer(poQuery.get(), eDataType, osAttrName, pData, nValues);
280✔
318

319
            // write additional co-registered values
320
            std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
560✔
321

322
            if (poGDS->m_lpoAttributeDS.size() > 0)
280✔
323
            {
324
                const int nXSizeToRead = nXOff + nXSize > nRasterXSize
12✔
325
                                             ? nRasterXSize - nXOff
6✔
326
                                             : nXSize;
327
                const int nYSizeToRead = nYOff + nYSize > nRasterYSize
12✔
328
                                             ? nRasterYSize - nYOff
6✔
329
                                             : nYSize;
330
                for (auto const &poAttrDS : poGDS->m_lpoAttributeDS)
18✔
331
                {
332
                    GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(nBand);
12✔
333
                    GDALDataType eAttrType = poAttrBand->GetRasterDataType();
12✔
334
                    int nBytes = GDALGetDataTypeSizeBytes(eAttrType);
12✔
335
                    void *pAttrBlock = VSI_MALLOC_VERBOSE(nBytes * nValues);
12✔
336

337
                    if (pAttrBlock == nullptr)
12✔
338
                    {
339
                        CPLError(CE_Failure, CPLE_OutOfMemory,
×
340
                                 "Cannot allocate attribute buffer");
341
                        return CE_Failure;
×
342
                    }
343
                    aBlocks.emplace_back(pAttrBlock, &VSIFree);
12✔
344

345
                    poAttrBand->AdviseRead(nXOff, nYOff, nXSizeToRead,
12✔
346
                                           nYSizeToRead, nXSizeToRead,
347
                                           nYSizeToRead, eAttrType, nullptr);
12✔
348

349
                    CPLErr eErr = poAttrBand->RasterIO(
12✔
350
                        GF_Read, nXOff, nYOff, nXSizeToRead, nYSizeToRead,
351
                        pAttrBlock, nXSizeToRead, nYSizeToRead, eAttrType,
352
                        nPixelSpace, nLineSpace, psExtraArg);
353

354
                    if (eErr == CE_None)
12✔
355
                    {
356
                        CPLString osName =
357
                            CPLGetBasenameSafe(poAttrDS->GetDescription());
24✔
358

359
                        SetBuffer(poQuery.get(), eAttrType, osName, pAttrBlock,
12✔
360
                                  nValues);
361
                    }
362
                    else
363
                    {
364
                        return eErr;
×
365
                    }
366
                }
367
            }
368

369
            if (bStats)
280✔
370
                tiledb::Stats::enable();
×
371

372
            auto status = poQuery->submit();
280✔
373

374
            if (bStats)
280✔
375
            {
376
                tiledb::Stats::dump(stdout);
×
377
                tiledb::Stats::disable();
×
378
            }
379

380
            if (status == tiledb::Query::Status::FAILED)
280✔
381
                return CE_Failure;
×
382
            else
383
                return CE_None;
280✔
384
        }
385
        catch (const tiledb::TileDBError &e)
×
386
        {
387
            CPLError(CE_Failure, CPLE_AppDefined,
×
388
                     "TileDB: TileDBRasterBand::IRasterIO() failed: %s",
389
                     e.what());
×
390
            return CE_Failure;
×
391
        }
392
    }
393

394
    return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
63✔
395
                                        pData, nBufXSize, nBufYSize, eBufType,
396
                                        nPixelSpace, nLineSpace, psExtraArg);
63✔
397
}
398

399
CPLErr TileDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
140✔
400
                                    void *pImage)
401
{
402
    const int nXOff = nBlockXOff * nBlockXSize;
140✔
403
    const int nYOff = nBlockYOff * nBlockYSize;
140✔
404
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
140✔
405
    return IRasterIO(GF_Read, nXOff, nYOff, nBlockXSize, nBlockYSize, pImage,
280✔
406
                     nBlockXSize, nBlockYSize, eDataType, nDTSize,
407
                     nDTSize * nBlockXSize, nullptr);
140✔
408
}
409

410
/************************************************************************/
411
/*                             IWriteBlock()                            */
412
/************************************************************************/
413

414
CPLErr TileDBRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
15✔
415
                                     void *pImage)
416

417
{
418
    if (eAccess == GA_ReadOnly)
15✔
419
    {
420
        CPLError(CE_Failure, CPLE_NoWriteAccess,
×
421
                 "Unable to write block, dataset is opened read only.\n");
422
        return CE_Failure;
×
423
    }
424

425
    CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
15✔
426
              pImage != nullptr);
427

428
    int nStartX = nBlockXSize * nBlockXOff;
15✔
429
    int nStartY = nBlockYSize * nBlockYOff;
15✔
430

431
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
15✔
432
    return IRasterIO(GF_Write, nStartX, nStartY, nBlockXSize, nBlockYSize,
30✔
433
                     pImage, nBlockXSize, nBlockYSize, eDataType, nDTSize,
434
                     nDTSize * nBlockXSize, nullptr);
15✔
435
}
436

437
/************************************************************************/
438
/*                       GetColorInterpretation()                       */
439
/************************************************************************/
440

441
GDALColorInterp TileDBRasterBand::GetColorInterpretation()
70✔
442

443
{
444
    if (poGDS->nBands == 1)
70✔
445
        return GCI_GrayIndex;
20✔
446

447
    if (nBand == 1)
50✔
448
        return GCI_RedBand;
20✔
449

450
    else if (nBand == 2)
30✔
451
        return GCI_GreenBand;
15✔
452

453
    else if (nBand == 3)
15✔
454
        return GCI_BlueBand;
15✔
455

456
    return GCI_AlphaBand;
×
457
}
458

459
/************************************************************************/
460
/*                           GetNoDataValue()                           */
461
/************************************************************************/
462

463
double TileDBRasterBand::GetNoDataValue(int *pbHasNoData)
79✔
464
{
465
    if (pbHasNoData)
79✔
466
        *pbHasNoData = false;
76✔
467
    if (m_bNoDataSet)
79✔
468
    {
469
        if (pbHasNoData)
11✔
470
            *pbHasNoData = true;
11✔
471
        return m_dfNoData;
11✔
472
    }
473
    if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
68✔
474
        return 0.0;
×
475
    if (!poGDS->m_array)
68✔
476
        return 0.0;
×
477
    double dfNoData = 0.0;
68✔
478
    try
479
    {
480
        const void *value = nullptr;
68✔
481
        uint64_t size = 0;
68✔
482
        // Caution: 2 below statements must not be combined in a single one,
483
        // as the lifetime of value is linked to the return value of
484
        // attribute()
485
        auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
68✔
486
                        ->schema()
68✔
487
                        .attribute(osAttrName);
136✔
488
        attr.get_fill_value(&value, &size);
68✔
489
        if (value &&
68✔
490
            size == static_cast<uint64_t>(GDALGetDataTypeSizeBytes(eDataType)))
68✔
491
        {
492
            switch (eDataType)
68✔
493
            {
494
                case GDT_Byte:
51✔
495
                    dfNoData = *static_cast<const uint8_t *>(value);
51✔
496
                    break;
51✔
497
                case GDT_Int8:
1✔
498
                    dfNoData = *static_cast<const int8_t *>(value);
1✔
499
                    break;
1✔
500
                case GDT_UInt16:
1✔
501
                    dfNoData = *static_cast<const uint16_t *>(value);
1✔
502
                    break;
1✔
503
                case GDT_Int16:
2✔
504
                case GDT_CInt16:
505
                    dfNoData = *static_cast<const int16_t *>(value);
2✔
506
                    break;
2✔
507
                case GDT_UInt32:
1✔
508
                    dfNoData = *static_cast<const uint32_t *>(value);
1✔
509
                    break;
1✔
510
                case GDT_Int32:
2✔
511
                case GDT_CInt32:
512
                    dfNoData = *static_cast<const int32_t *>(value);
2✔
513
                    break;
2✔
514
                case GDT_UInt64:
×
515
                    dfNoData = static_cast<double>(
×
516
                        *static_cast<const uint64_t *>(value));
×
517
                    break;
×
518
                case GDT_Int64:
×
519
                    dfNoData = static_cast<double>(
×
520
                        *static_cast<const int64_t *>(value));
×
521
                    break;
×
522
                case GDT_Float16:
×
523
                case GDT_CFloat16:
524
                    // tileDB does not support float16
525
                    CPLAssert(false);
×
526
                    break;
527
                case GDT_Float32:
5✔
528
                case GDT_CFloat32:
529
                    dfNoData = *static_cast<const float *>(value);
5✔
530
                    break;
5✔
531
                case GDT_Float64:
5✔
532
                case GDT_CFloat64:
533
                    dfNoData = *static_cast<const double *>(value);
5✔
534
                    break;
5✔
535
                case GDT_Unknown:
×
536
                case GDT_TypeCount:
537
                    break;
×
538
            }
539
            if (pbHasNoData)
68✔
540
                *pbHasNoData = true;
65✔
541
        }
542
    }
543
    catch (const tiledb::TileDBError &e)
×
544
    {
545
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
546
    }
547
    return dfNoData;
68✔
548
}
549

550
/************************************************************************/
551
/*                           IsValidNoData()                            */
552
/************************************************************************/
553

554
template <class T> static bool IsValidNoData(double dfNoData)
37✔
555
{
556
    return GDALIsValueInRange<T>(dfNoData) &&
72✔
557
           dfNoData == static_cast<double>(static_cast<T>(dfNoData));
72✔
558
}
559

560
/************************************************************************/
561
/*                           SetNoDataValue()                           */
562
/************************************************************************/
563

564
CPLErr TileDBRasterBand::SetNoDataValue(double dfNoData)
45✔
565
{
566
    if (poGDS->m_bDeferredCreateHasBeenSuccessful)
45✔
567
    {
568
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
569
                 "TileDBRasterBand::SetNoDataValue(): cannot be called after "
570
                 "pixel values have been set");
571
        return CE_Failure;
1✔
572
    }
573

574
    if (nBand != 1 &&
56✔
575
        dfNoData !=
576
            cpl::down_cast<TileDBRasterBand *>(poGDS->papoBands[0])->m_dfNoData)
12✔
577
    {
578
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
579
                 "TileDBRasterBand::SetNoDataValue(): all bands should have "
580
                 "the same nodata value");
581
        return CE_Failure;
1✔
582
    }
583

584
    bool bIsValid = false;
43✔
585
    switch (eDataType)
43✔
586
    {
587
        case GDT_Byte:
26✔
588
            bIsValid = IsValidNoData<uint8_t>(dfNoData);
26✔
589
            break;
26✔
590
        case GDT_Int8:
1✔
591
            bIsValid = IsValidNoData<int8_t>(dfNoData);
1✔
592
            break;
1✔
593
        case GDT_UInt16:
1✔
594
            bIsValid = IsValidNoData<uint16_t>(dfNoData);
1✔
595
            break;
1✔
596
        case GDT_Int16:
2✔
597
        case GDT_CInt16:
598
            bIsValid = IsValidNoData<int16_t>(dfNoData);
2✔
599
            break;
2✔
600
        case GDT_UInt32:
1✔
601
            bIsValid = IsValidNoData<uint32_t>(dfNoData);
1✔
602
            break;
1✔
603
        case GDT_Int32:
2✔
604
        case GDT_CInt32:
605
            bIsValid = IsValidNoData<int32_t>(dfNoData);
2✔
606
            break;
2✔
607
        case GDT_UInt64:
×
608
            bIsValid = IsValidNoData<uint64_t>(dfNoData);
×
609
            break;
×
610
        case GDT_Int64:
×
611
            bIsValid = IsValidNoData<int64_t>(dfNoData);
×
612
            break;
×
613
        case GDT_Float16:
×
614
        case GDT_CFloat16:
615
            // tileDB does not support float16
616
            bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
×
617
            break;
×
618
        case GDT_Float32:
5✔
619
        case GDT_CFloat32:
620
            bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
5✔
621
            break;
5✔
622
        case GDT_Float64:
5✔
623
        case GDT_CFloat64:
624
            bIsValid = true;
5✔
625
            break;
5✔
626
        case GDT_Unknown:
×
627
        case GDT_TypeCount:
628
            break;
×
629
    }
630
    if (!bIsValid)
43✔
631
    {
632
        CPLError(CE_Failure, CPLE_NotSupported,
3✔
633
                 "TileDBRasterBand::SetNoDataValue(): nodata value cannot be "
634
                 "stored in band data type");
635
        return CE_Failure;
3✔
636
    }
637

638
    m_dfNoData = dfNoData;
40✔
639
    m_bNoDataSet = true;
40✔
640
    return CE_None;
40✔
641
}
642

643
/************************************************************************/
644
/*                           GetOverviewCount()                         */
645
/************************************************************************/
646

647
int TileDBRasterBand::GetOverviewCount()
15✔
648
{
649
    if (poGDS->m_nOverviewCountFromMetadata > 0)
15✔
650
    {
651
        poGDS->LoadOverviews();
7✔
652
        return static_cast<int>(poGDS->m_apoOverviewDS.size());
7✔
653
    }
654
    return GDALPamRasterBand::GetOverviewCount();
8✔
655
}
656

657
/************************************************************************/
658
/*                              GetOverview()                           */
659
/************************************************************************/
660

661
GDALRasterBand *TileDBRasterBand::GetOverview(int nIdx)
22✔
662
{
663
    if (poGDS->m_nOverviewCountFromMetadata > 0)
22✔
664
    {
665
        poGDS->LoadOverviews();
21✔
666
        if (nIdx >= 0 && nIdx < static_cast<int>(poGDS->m_apoOverviewDS.size()))
21✔
667
        {
668
            return poGDS->m_apoOverviewDS[nIdx]->GetRasterBand(nBand);
17✔
669
        }
670
        return nullptr;
4✔
671
    }
672
    return GDALPamRasterBand::GetOverview(nIdx);
1✔
673
}
674

675
/************************************************************************/
676
/* ==================================================================== */
677
/*                     TileDBRasterDataset                              */
678
/* ==================================================================== */
679
/************************************************************************/
680

681
/************************************************************************/
682
/*                     ~TileDBRasterDataset()                           */
683
/************************************************************************/
684

685
TileDBRasterDataset::~TileDBRasterDataset()
498✔
686

687
{
688
    TileDBRasterDataset::CloseDependentDatasets();
249✔
689
    TileDBRasterDataset::Close();
249✔
690
}
498✔
691

692
/************************************************************************/
693
/*                              GetArray()                              */
694
/************************************************************************/
695

696
tiledb::Array &TileDBRasterDataset::GetArray(bool bForWrite,
307✔
697
                                             tiledb::Context *&ctx)
698
{
699
    if (eAccess == GA_Update && !bForWrite)
307✔
700
    {
701
        if (!m_roArray)
105✔
702
        {
703
            m_roCtx = std::make_unique<tiledb::Context>(m_ctx->config());
34✔
704
            if (nTimestamp)
34✔
705
            {
706
                m_roArray = std::make_unique<tiledb::Array>(
16✔
707
                    *m_roCtx, m_osArrayURI, TILEDB_READ,
8✔
708
                    tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
24✔
709
            }
710
            else
711
            {
712
                m_roArray = std::make_unique<tiledb::Array>(
52✔
713
                    *m_roCtx, m_osArrayURI, TILEDB_READ);
78✔
714
            }
715
        }
716

717
        ctx = m_roCtx.get();
105✔
718
        return *(m_roArray.get());
105✔
719
    }
720

721
    ctx = m_ctx.get();
202✔
722
    return *(m_array.get());
202✔
723
}
724

725
/************************************************************************/
726
/*                           IRasterio()                                */
727
/************************************************************************/
728

729
CPLErr TileDBRasterDataset::IRasterIO(
85✔
730
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
731
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
732
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
733
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
734

735
{
736
    if (!m_bDeferredCreateHasRun)
85✔
737
        DeferredCreate(/* bCreateArray = */ true);
48✔
738
    if (!m_bDeferredCreateHasBeenSuccessful)
85✔
739
        return CE_Failure;
×
740
    if (!m_array)
85✔
741
    {
742
        CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
1✔
743
        return CE_Failure;
1✔
744
    }
745

746
    // support special case of writing attributes for bands, all attributes have to be set at once
747
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
84✔
748

749
    if (eIndexMode == ATTRIBUTES && nBandCount == nBands &&
84✔
750
        eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
4✔
751
        nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
4✔
752
        nLineSpace == nBufXSize * nPixelSpace &&
4✔
753
        ((nBandSpace == 0 && nBandCount == 1) ||
1✔
754
         nBandSpace == nBufYSize * nLineSpace))
3✔
755
    {
756
        std::vector<uint64_t> oaSubarray = {
757
            static_cast<uint64_t>(nYOff),
4✔
758
            static_cast<uint64_t>(nYOff) + nYSize - 1,
4✔
759
            static_cast<uint64_t>(nXOff),
4✔
760
            static_cast<uint64_t>(nXOff) + nXSize - 1};
8✔
761

762
        try
763
        {
764
            tiledb::Context *ctx = m_ctx.get();
4✔
765
            const auto &oArray = GetArray(eRWFlag == GF_Write, ctx);
4✔
766

767
            auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
8✔
768
            tiledb::Subarray subarray(*ctx, oArray);
8✔
769
            subarray.set_subarray(oaSubarray);
4✔
770
            poQuery->set_subarray(subarray);
4✔
771

772
            for (int b = 0; b < nBandCount; b++)
14✔
773
            {
774
                TileDBRasterBand *poBand = cpl::down_cast<TileDBRasterBand *>(
10✔
775
                    GetRasterBand(panBandMap[b]));
10✔
776
                const size_t nRegionSize =
10✔
777
                    static_cast<size_t>(nBufXSize) * nBufYSize * nBufferDTSize;
10✔
778
                SetBuffer(poQuery.get(), eDataType, poBand->osAttrName,
10✔
779
                          static_cast<GByte *>(pData) + b * nRegionSize,
10✔
780
                          nRegionSize);
781
            }
782

783
            if (bStats)
4✔
784
                tiledb::Stats::enable();
×
785

786
            auto status = poQuery->submit();
4✔
787

788
            if (bStats)
4✔
789
            {
790
                tiledb::Stats::dump(stdout);
×
791
                tiledb::Stats::disable();
×
792
            }
793

794
            if (status == tiledb::Query::Status::FAILED)
4✔
795
                return CE_Failure;
×
796
            else
797
                return CE_None;
4✔
798
        }
799
        catch (const tiledb::TileDBError &e)
×
800
        {
801
            CPLError(CE_Failure, CPLE_AppDefined,
×
802
                     "TileDB: TileDBRasterDataset::IRasterIO() failed: %s",
803
                     e.what());
×
804
            return CE_Failure;
×
805
        }
806
    }
807

808
    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
80✔
809
                                     pData, nBufXSize, nBufYSize, eBufType,
810
                                     nBandCount, panBandMap, nPixelSpace,
811
                                     nLineSpace, nBandSpace, psExtraArg);
80✔
812
}
813

814
/************************************************************************/
815
/*                             AddDimensions()                          */
816
/************************************************************************/
817

818
CPLErr TileDBRasterDataset::AddDimensions(tiledb::Domain &domain,
117✔
819
                                          const char *pszAttrName,
820
                                          tiledb::Dimension &y,
821
                                          tiledb::Dimension &x,
822
                                          tiledb::Dimension *poBands)
823

824
{
825
    CPLErr eErr = CE_None;
117✔
826

827
    const bool bHasFillValue =
828
        nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_bNoDataSet
117✔
829
               : false;
117✔
830
    const double dfFillValue =
831
        nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_dfNoData
117✔
832
               : 0.0;
117✔
833

834
    switch (eIndexMode)
117✔
835
    {
836
        case ATTRIBUTES:
6✔
837
            domain.add_dimensions(y, x);
6✔
838
            eErr = CreateAttribute(eDataType, pszAttrName, nBands,
6✔
839
                                   bHasFillValue, dfFillValue);
840
            break;
6✔
841
        case PIXEL:
8✔
842
            assert(poBands);
8✔
843
            domain.add_dimensions(y, x, *poBands);
8✔
844
            eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
8✔
845
                                   dfFillValue);
846
            break;
8✔
847
        default:  // BAND
103✔
848
            assert(poBands);
103✔
849
            domain.add_dimensions(*poBands, y, x);
103✔
850
            eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
103✔
851
                                   dfFillValue);
852
            break;
103✔
853
    }
854

855
    return eErr;
117✔
856
}
857

858
/************************************************************************/
859
/*                              Close()                                 */
860
/************************************************************************/
861

862
CPLErr TileDBRasterDataset::Close()
487✔
863
{
864
    CPLErr eErr = CE_None;
487✔
865
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
487✔
866
    {
867
        if (TileDBRasterDataset::FlushCache(true) != CE_None)
249✔
868
            eErr = CE_Failure;
×
869

870
        if (!m_bDeferredCreateHasRun)
249✔
871
            DeferredCreate(/* bCreateArray = */ true);
62✔
872

873
        if (nPamFlags & GPF_DIRTY)
249✔
874
            TrySaveXML();
135✔
875

876
        try
877
        {
878
            if (m_array)
249✔
879
            {
880
                m_array->close();
246✔
881
                m_array.reset();
246✔
882
            }
883
        }
884
        catch (const tiledb::TileDBError &e)
×
885
        {
886
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
887
        }
888
        try
889
        {
890
            if (m_roArray)
249✔
891
            {
892
                m_roArray->close();
34✔
893
                m_roArray.reset();
34✔
894
            }
895
        }
896
        catch (const tiledb::TileDBError &e)
×
897
        {
898
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
899
        }
900

901
        if (GDALPamDataset::Close() != CE_None)
249✔
902
            eErr = CE_Failure;
×
903
    }
904
    return eErr;
487✔
905
}
906

907
/************************************************************************/
908
/*                       CloseDependentDatasets()                       */
909
/************************************************************************/
910

911
int TileDBRasterDataset::CloseDependentDatasets()
249✔
912
{
913
    int bDroppedRef = GDALPamDataset::CloseDependentDatasets();
249✔
914
    if (!m_apoOverviewDS.empty())
249✔
915
        bDroppedRef = TRUE;
6✔
916
    m_apoOverviewDS.clear();
249✔
917
    return bDroppedRef;
249✔
918
}
919

920
/************************************************************************/
921
/*                             FlushCache()                             */
922
/************************************************************************/
923

924
CPLErr TileDBRasterDataset::FlushCache(bool bAtClosing)
315✔
925

926
{
927
    return BlockBasedFlushCache(bAtClosing);
315✔
928
}
929

930
/************************************************************************/
931
/*                           TrySaveXML()                               */
932
/************************************************************************/
933

934
CPLErr TileDBRasterDataset::TrySaveXML()
135✔
935

936
{
937
    if (m_array == nullptr)
135✔
938
        return CE_None;
×
939

940
    CPLXMLNode *psTree = nullptr;
135✔
941
    try
942
    {
943
        nPamFlags &= ~GPF_DIRTY;
135✔
944

945
        if (psPam == nullptr || (nPamFlags & GPF_NOSAVE))
135✔
946
            return CE_None;
×
947

948
        /* --------------------------------------------------------------------
949
         */
950
        /*      Make sure we know the filename we want to store in. */
951
        /* --------------------------------------------------------------------
952
         */
953
        if (!BuildPamFilename())
135✔
954
            return CE_None;
×
955

956
        /* --------------------------------------------------------------------
957
         */
958
        /*      Build the XML representation of the auxiliary metadata. */
959
        /* --------------------------------------------------------------------
960
         */
961
        psTree = SerializeToXML(nullptr);
135✔
962

963
        if (psTree == nullptr)
135✔
964
        {
965
            /* If we have unset all metadata, we have to delete the PAM file */
966
            m_array->delete_metadata(GDAL_ATTRIBUTE_NAME);
×
967

968
            if (m_bDatasetInGroup)
×
969
            {
970
                tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
×
971
                group.delete_metadata(GDAL_ATTRIBUTE_NAME);
×
972
            }
973

974
            return CE_None;
×
975
        }
976

977
        if (m_poSubDatasetsTree)
135✔
978
        {
979
            CPLAddXMLChild(psTree,
3✔
980
                           CPLCloneXMLTree(m_poSubDatasetsTree->psChild));
3✔
981
        }
982

983
        /* --------------------------------------------------------------------
984
         */
985
        /*      If we are working with a subdataset, we need to integrate */
986
        /*      the subdataset tree within the whole existing pam tree, */
987
        /*      after removing any old version of the same subdataset. */
988
        /* --------------------------------------------------------------------
989
         */
990
        if (!psPam->osSubdatasetName.empty())
135✔
991
        {
992
            CPLXMLNode *psOldTree, *psSubTree;
993

994
            CPLErrorReset();
×
995
            {
996
                CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
×
997
                psOldTree = CPLParseXMLFile(psPam->pszPamFilename);
×
998
            }
999

1000
            if (psOldTree == nullptr)
×
1001
                psOldTree =
1002
                    CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset");
×
1003

1004
            for (psSubTree = psOldTree->psChild; psSubTree != nullptr;
×
1005
                 psSubTree = psSubTree->psNext)
×
1006
            {
1007
                if (psSubTree->eType != CXT_Element ||
×
1008
                    !EQUAL(psSubTree->pszValue, "Subdataset"))
×
1009
                    continue;
×
1010

1011
                if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
×
1012
                           psPam->osSubdatasetName))
1013
                    continue;
×
1014

1015
                break;
×
1016
            }
1017

1018
            if (psSubTree == nullptr)
×
1019
            {
1020
                psSubTree =
1021
                    CPLCreateXMLNode(psOldTree, CXT_Element, "Subdataset");
×
1022
                CPLCreateXMLNode(
×
1023
                    CPLCreateXMLNode(psSubTree, CXT_Attribute, "name"),
1024
                    CXT_Text, psPam->osSubdatasetName);
×
1025
            }
1026

1027
            CPLXMLNode *psOldPamDataset =
1028
                CPLGetXMLNode(psSubTree, "PAMDataset");
×
1029
            if (psOldPamDataset != nullptr)
×
1030
            {
1031
                CPLRemoveXMLChild(psSubTree, psOldPamDataset);
×
1032
                CPLDestroyXMLNode(psOldPamDataset);
×
1033
            }
1034

1035
            CPLAddXMLChild(psSubTree, psTree);
×
1036
            psTree = psOldTree;
×
1037
        }
1038

1039
        if (m_nOverviewCountFromMetadata)
135✔
1040
        {
1041
            CPLCreateXMLElementAndValue(
4✔
1042
                psTree, OVERVIEW_COUNT_KEY,
1043
                CPLSPrintf("%d", m_nOverviewCountFromMetadata));
1044
        }
1045

1046
        /* --------------------------------------------------------------------
1047
         */
1048
        /*      Try saving the auxiliary metadata. */
1049
        /* --------------------------------------------------------------------
1050
         */
1051
        CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
270✔
1052
        char *pszTree = CPLSerializeXMLTree(psTree);
135✔
1053

1054
        std::unique_ptr<tiledb::Array> poArrayToClose;
×
1055
        tiledb::Array *poArray = m_array.get();
135✔
1056
        if (eAccess == GA_ReadOnly)
135✔
1057
        {
1058
            if (nTimestamp)
1✔
1059
            {
1060
                poArrayToClose = std::make_unique<tiledb::Array>(
×
1061
                    *m_ctx, m_array->uri(), TILEDB_WRITE,
×
1062
                    tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
×
1063
            }
1064
            else
1065
            {
1066
                poArrayToClose = std::make_unique<tiledb::Array>(
2✔
1067
                    *m_ctx, m_array->uri(), TILEDB_WRITE);
3✔
1068
            }
1069
            poArray = poArrayToClose.get();
1✔
1070
        }
1071

1072
        poArray->put_metadata(DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
135✔
1073
                              static_cast<int>(strlen(RASTER_DATASET_TYPE)),
1074
                              RASTER_DATASET_TYPE);
1075
        poArray->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
135✔
1076
                              static_cast<int>(strlen(pszTree)), pszTree);
135✔
1077

1078
        if (poArrayToClose)
135✔
1079
            poArrayToClose->close();
1✔
1080

1081
        if (m_bDatasetInGroup)
135✔
1082
        {
1083
            tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
369✔
1084
            group.put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
123✔
1085
                               static_cast<int>(strlen(pszTree)), pszTree);
123✔
1086
            group.close();
123✔
1087
        }
1088

1089
        CPLFree(pszTree);
135✔
1090

1091
        /* --------------------------------------------------------------------
1092
         */
1093
        /*      Cleanup */
1094
        /* --------------------------------------------------------------------
1095
         */
1096
        if (psTree)
135✔
1097
            CPLDestroyXMLNode(psTree);
135✔
1098

1099
        return CE_None;
135✔
1100
    }
1101
    catch (const std::exception &e)
×
1102
    {
1103
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
1104
        if (psTree)
×
1105
            CPLDestroyXMLNode(psTree);
×
1106
        return CE_Failure;
×
1107
    }
1108
}
1109

1110
/************************************************************************/
1111
/*                           TryLoadXML()                               */
1112
/************************************************************************/
1113

1114
CPLErr TileDBRasterDataset::TryLoadXML(CSLConstList papszSiblingFiles)
129✔
1115

1116
{
1117
    return TryLoadCachedXML(papszSiblingFiles, true);
129✔
1118
}
1119

1120
/************************************************************************/
1121
/*                           TryLoadCachedXML()                               */
1122
/************************************************************************/
1123

1124
CPLErr TileDBRasterDataset::TryLoadCachedXML(CSLConstList /*papszSiblingFiles*/,
257✔
1125
                                             bool bReload)
1126

1127
{
1128
    CPLXMLNode *psTree = nullptr;
257✔
1129
    try
1130
    {
1131
        PamInitialize();
257✔
1132
        tiledb::VFS vfs(*m_ctx, m_ctx->config());
514✔
1133

1134
        /* --------------------------------------------------------------------
1135
         */
1136
        /*      Clear dirty flag.  Generally when we get to this point is */
1137
        /*      from a call at the end of the Open() method, and some calls */
1138
        /*      may have already marked the PAM info as dirty (for instance */
1139
        /*      setting metadata), but really everything to this point is */
1140
        /*      reproducible, and so the PAM info should not really be */
1141
        /*      thought of as dirty. */
1142
        /* --------------------------------------------------------------------
1143
         */
1144
        nPamFlags &= ~GPF_DIRTY;
257✔
1145

1146
        /* --------------------------------------------------------------------
1147
         */
1148
        /*      Try reading the file. */
1149
        /* --------------------------------------------------------------------
1150
         */
1151
        if (!BuildPamFilename())
257✔
1152
            return CE_None;
×
1153

1154
        /* --------------------------------------------------------------------
1155
         */
1156
        /*      In case the PAM filename is a .aux.xml file next to the */
1157
        /*      physical file and we have a siblings list, then we can skip */
1158
        /*      stat'ing the filesystem. */
1159
        /* --------------------------------------------------------------------
1160
         */
1161
        CPLErr eLastErr = CPLGetLastErrorType();
257✔
1162
        int nLastErrNo = CPLGetLastErrorNo();
257✔
1163
        CPLString osLastErrorMsg = CPLGetLastErrorMsg();
514✔
1164

1165
        CPLErrorReset();
257✔
1166
        {
1167
            CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
514✔
1168

1169
            if (bReload)
257✔
1170
            {
1171
                tiledb_datatype_t v_type =
129✔
1172
                    TILEDB_UINT8;  // CPLSerializeXMLTree returns char*
1173
                const void *v_r = nullptr;
129✔
1174
                uint32_t v_num = 0;
129✔
1175
                auto &oArray = ((eAccess == GA_Update) && (m_roArray))
23✔
1176
                                   ? m_roArray
1177
                                   : m_array;
152✔
1178
                oArray->get_metadata(GDAL_ATTRIBUTE_NAME, &v_type, &v_num,
129✔
1179
                                     &v_r);
1180
                if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
128✔
1181
                            v_type == TILEDB_STRING_ASCII ||
×
1182
                            v_type == TILEDB_STRING_UTF8))
129✔
1183
                {
1184
                    osMetaDoc =
1185
                        CPLString(static_cast<const char *>(v_r), v_num);
128✔
1186
                }
1187
                psTree = CPLParseXMLString(osMetaDoc);
129✔
1188
            }
1189

1190
            if (bReload && psTree == nullptr &&
258✔
1191
                vfs.is_file(psPam->pszPamFilename))
258✔
1192
            {
1193
                auto nBytes = vfs.file_size(psPam->pszPamFilename);
1✔
1194
                tiledb::VFS::filebuf fbuf(vfs);
2✔
1195
                fbuf.open(psPam->pszPamFilename, std::ios::in);
1✔
1196
                std::istream is(&fbuf);
1✔
1197
                osMetaDoc.resize(nBytes);
1✔
1198
                is.read(&osMetaDoc[0], nBytes);
1✔
1199
                fbuf.close();
1✔
1200
                psTree = CPLParseXMLString(osMetaDoc);
1✔
1201
            }
1202

1203
            if (!bReload)
257✔
1204
            {
1205
                psTree = CPLParseXMLString(osMetaDoc);
128✔
1206
            }
1207
        }
1208
        CPLErrorReset();
257✔
1209

1210
        if (eLastErr != CE_None)
257✔
1211
            CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
×
1212

1213
        /* --------------------------------------------------------------------
1214
         */
1215
        /*      If we are looking for a subdataset, search for its subtree not.
1216
         */
1217
        /* --------------------------------------------------------------------
1218
         */
1219
        if (psTree && !psPam->osSubdatasetName.empty())
257✔
1220
        {
1221
            CPLXMLNode *psSubTree = psTree->psChild;
11✔
1222

1223
            for (; psSubTree != nullptr; psSubTree = psSubTree->psNext)
65✔
1224
            {
1225
                if (psSubTree->eType != CXT_Element ||
64✔
1226
                    !EQUAL(psSubTree->pszValue, "Subdataset"))
64✔
1227
                    continue;
38✔
1228

1229
                if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
26✔
1230
                           psPam->osSubdatasetName))
1231
                    continue;
16✔
1232

1233
                psSubTree = CPLGetXMLNode(psSubTree, "PAMDataset");
10✔
1234
                break;
10✔
1235
            }
1236

1237
            if (psSubTree != nullptr)
11✔
1238
                psSubTree = CPLCloneXMLTree(psSubTree);
10✔
1239

1240
            CPLDestroyXMLNode(psTree);
11✔
1241
            psTree = psSubTree;
11✔
1242
        }
1243
        if (psTree == nullptr)
257✔
1244
            return CE_Failure;
1✔
1245

1246
        m_nOverviewCountFromMetadata =
256✔
1247
            atoi(CPLGetXMLValue(psTree, OVERVIEW_COUNT_KEY, "0"));
256✔
1248
        if (m_nOverviewCountFromMetadata)
256✔
1249
        {
1250
            CPLDebugOnly("TileDB", "OverviewCount = %d",
10✔
1251
                         m_nOverviewCountFromMetadata);
1252
        }
1253

1254
        /* --------------------------------------------------------------------
1255
         */
1256
        /*      Initialize ourselves from this XML tree. */
1257
        /* --------------------------------------------------------------------
1258
         */
1259

1260
        CPLString osPath(CPLGetPathSafe(psPam->pszPamFilename));
256✔
1261
        const CPLErr eErr = XMLInit(psTree, osPath);
256✔
1262

1263
        CPLDestroyXMLNode(psTree);
256✔
1264

1265
        if (eErr != CE_None)
256✔
1266
            PamClear();
×
1267

1268
        return eErr;
256✔
1269
    }
1270
    catch (const tiledb::TileDBError &e)
×
1271
    {
1272
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
1273
        if (psTree)
×
1274
            CPLDestroyXMLNode(psTree);
×
1275
        return CE_Failure;
×
1276
    }
1277
}
1278

1279
/************************************************************************/
1280
/*                             GetMetadata()                            */
1281
/************************************************************************/
1282

1283
char **TileDBRasterDataset::GetMetadata(const char *pszDomain)
216✔
1284

1285
{
1286
    if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
216✔
1287
    {
1288
        if (m_aosSubdatasetMD.empty())
6✔
1289
        {
1290
            CSLConstList papszMD = GDALPamDataset::GetMetadata(pszDomain);
5✔
1291
            for (const auto &[pszKey, pszValue] :
8✔
1292
                 cpl::IterateNameValue(papszMD))
13✔
1293
            {
1294
                if (STARTS_WITH(pszKey, "SUBDATASET_") &&
4✔
1295
                    EQUAL(pszKey + strlen(pszKey) - strlen("_NAME"), "_NAME") &&
4✔
1296
                    !STARTS_WITH(pszValue, "TILEDB:"))
2✔
1297
                {
1298
                    m_aosSubdatasetMD.AddNameValue(
2✔
1299
                        pszKey, CPLString().Printf("TILEDB:\"%s\":%s",
2✔
1300
                                                   GetDescription(), pszValue));
2✔
1301
                }
1302
                else
1303
                {
1304
                    m_aosSubdatasetMD.AddNameValue(pszKey, pszValue);
2✔
1305
                }
1306
            }
1307
        }
1308
        return m_aosSubdatasetMD.List();
6✔
1309
    }
1310
    else
1311
    {
1312
        return GDALPamDataset::GetMetadata(pszDomain);
210✔
1313
    }
1314
}
1315

1316
/************************************************************************/
1317
/*                                Open()                                */
1318
/************************************************************************/
1319

1320
GDALDataset *TileDBRasterDataset::Open(GDALOpenInfo *poOpenInfo,
129✔
1321
                                       tiledb::Object::Type objectType)
1322

1323
{
1324
    try
1325
    {
1326
        return OpenInternal(poOpenInfo, objectType);
129✔
1327
    }
1328
    catch (const tiledb::TileDBError &e)
×
1329
    {
1330
        CPLError(CE_Failure, CPLE_AppDefined, "TileDB: Open(%s) failed: %s",
×
1331
                 poOpenInfo->pszFilename, e.what());
×
1332
        return nullptr;
×
1333
    }
1334
}
1335

1336
/************************************************************************/
1337
/*                             OpenInternal()                           */
1338
/************************************************************************/
1339

1340
GDALDataset *TileDBRasterDataset::OpenInternal(GDALOpenInfo *poOpenInfo,
129✔
1341
                                               tiledb::Object::Type objectType)
1342

1343
{
1344
    auto poDS = std::make_unique<TileDBRasterDataset>();
258✔
1345

1346
    poDS->m_bDeferredCreateHasRun = true;
129✔
1347
    poDS->m_bDeferredCreateHasBeenSuccessful = true;
129✔
1348

1349
    const char *pszConfig =
1350
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
129✔
1351

1352
    const char *pszTimestamp =
1353
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_TIMESTAMP");
129✔
1354

1355
    poDS->bStats =
258✔
1356
        CSLFetchBoolean(poOpenInfo->papszOpenOptions, "STATS", FALSE);
129✔
1357

1358
    if (pszConfig != nullptr)
129✔
1359
    {
1360
        poDS->m_osConfigFilename = pszConfig;
×
1361
        tiledb::Config cfg(pszConfig);
×
1362
        poDS->m_ctx.reset(new tiledb::Context(cfg));
×
1363
    }
1364
    else
1365
    {
1366
        poDS->m_ctx.reset(new tiledb::Context());
129✔
1367
    }
1368
    if (pszTimestamp)
129✔
1369
        poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
18✔
1370

1371
    CPLString osURI;
258✔
1372
    CPLString osSubdataset;
258✔
1373

1374
    std::string osAttrNameTmp;
258✔
1375
    const char *pszAttr =
1376
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_ATTRIBUTE");
129✔
1377

1378
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
129✔
1379
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
2✔
1380
    {
1381
        // form required read attributes and open file
1382
        // Create a corresponding GDALDataset.
1383
        CPLStringList apszName(
1384
            CSLTokenizeString2(poOpenInfo->pszFilename, ":",
2✔
1385
                               CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
2✔
1386

1387
        if (apszName.size() != 3)
2✔
1388
        {
1389
            return nullptr;
×
1390
        }
1391

1392
        osURI = TileDBDataset::VSI_to_tiledb_uri(apszName[1]);
2✔
1393
        osSubdataset = apszName[2];
2✔
1394
        poDS->SetSubdatasetName(osSubdataset.c_str());
4✔
1395
    }
1396
    else
1397
    {
1398
        if (pszAttr != nullptr)
127✔
1399
        {
1400
            poDS->SetSubdatasetName(pszAttr);
4✔
1401
        }
1402

1403
        osURI = TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
127✔
1404
    }
1405

1406
    CPLString osAux = CPLGetBasenameSafe(osURI);
258✔
1407
    osAux += ".tdb";
129✔
1408

1409
    // aux file is in array folder
1410
    poDS->SetPhysicalFilename(
258✔
1411
        CPLFormFilenameSafe(osURI, osAux, nullptr).c_str());
258✔
1412
    // Initialize any PAM information.
1413
    poDS->SetDescription(osURI);
129✔
1414

1415
    poDS->m_osArrayURI = osURI;
129✔
1416
    if (objectType == tiledb::Object::Type::Group)
129✔
1417
    {
1418
        poDS->m_bDatasetInGroup = true;
116✔
1419

1420
        // Full resolution raster array
1421
        poDS->m_osArrayURI = CPLFormFilenameSafe(osURI.c_str(), "l_0", nullptr);
116✔
1422
    }
1423

1424
    const tiledb_query_type_t eMode =
129✔
1425
        poOpenInfo->eAccess == GA_Update ? TILEDB_WRITE : TILEDB_READ;
129✔
1426

1427
    if (poDS->nTimestamp)
129✔
1428
    {
1429
        poDS->m_array = std::make_unique<tiledb::Array>(
36✔
1430
            *poDS->m_ctx, poDS->m_osArrayURI, eMode,
18✔
1431
            tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp));
54✔
1432
    }
1433
    else
1434
    {
1435
        poDS->m_array = std::make_unique<tiledb::Array>(
222✔
1436
            *poDS->m_ctx, poDS->m_osArrayURI, eMode);
222✔
1437
    }
1438

1439
    poDS->eAccess = poOpenInfo->eAccess;
129✔
1440

1441
    // Force opening read-only dataset
1442
    if (eMode == TILEDB_WRITE)
129✔
1443
    {
1444
        tiledb::Context *ctx = nullptr;
23✔
1445
        poDS->GetArray(false, ctx);
23✔
1446
    }
1447

1448
    // dependent on PAM metadata for information about array
1449
    poDS->TryLoadXML();
129✔
1450

1451
    tiledb::ArraySchema schema = poDS->m_array->schema();
258✔
1452

1453
    char **papszStructMeta = poDS->GetMetadata("IMAGE_STRUCTURE");
129✔
1454
    const char *pszXSize = CSLFetchNameValue(papszStructMeta, "X_SIZE");
129✔
1455
    if (pszXSize)
129✔
1456
    {
1457
        poDS->nRasterXSize = atoi(pszXSize);
113✔
1458
        if (poDS->nRasterXSize <= 0)
113✔
1459
        {
1460
            CPLError(CE_Failure, CPLE_AppDefined,
×
1461
                     "Width %i should be greater than zero.",
1462
                     poDS->nRasterXSize);
×
1463
            return nullptr;
×
1464
        }
1465
    }
1466

1467
    const char *pszYSize = CSLFetchNameValue(papszStructMeta, "Y_SIZE");
129✔
1468
    if (pszYSize)
129✔
1469
    {
1470
        poDS->nRasterYSize = atoi(pszYSize);
113✔
1471
        if (poDS->nRasterYSize <= 0)
113✔
1472
        {
1473
            CPLError(CE_Failure, CPLE_AppDefined,
×
1474
                     "Height %i should be greater than zero.",
1475
                     poDS->nRasterYSize);
×
1476
            return nullptr;
×
1477
        }
1478
    }
1479

1480
    const char *pszNBits = CSLFetchNameValue(papszStructMeta, "NBITS");
129✔
1481
    if (pszNBits)
129✔
1482
    {
1483
        poDS->nBitsPerSample = atoi(pszNBits);
113✔
1484
    }
1485

1486
    const char *pszDataType = CSLFetchNameValue(papszStructMeta, "DATA_TYPE");
129✔
1487
    if (pszDataType)
129✔
1488
    {
1489
        // handle the case where arrays have been written with int type
1490
        // (2.5.0)
1491
        GDALDataType eDT = GDALGetDataTypeByName(pszDataType);
113✔
1492
        if (eDT == GDT_Unknown)
113✔
1493
        {
1494
            int t = atoi(pszDataType);
1✔
1495
            if ((t > 0) && (t < GDT_TypeCount))
1✔
1496
                poDS->eDataType = static_cast<GDALDataType>(atoi(pszDataType));
1✔
1497
            else
1498
            {
1499
                CPLError(CE_Failure, CPLE_AppDefined, "Unknown data type %s.",
×
1500
                         pszDataType);
1501
                return nullptr;
×
1502
            }
1503
        }
1504
        else
1505
        {
1506
            poDS->eDataType = eDT;
112✔
1507
        }
1508
    }
1509
    else
1510
    {
1511
        if (!pszAttr)
16✔
1512
        {
1513
            if (schema.attribute_num() == 1)
16✔
1514
            {
1515
                osAttrNameTmp = schema.attribute(0).name();
14✔
1516
                pszAttr = osAttrNameTmp.c_str();
14✔
1517
            }
1518
        }
1519
    }
1520

1521
    const char *pszIndexMode = CSLFetchNameValue(papszStructMeta, "INTERLEAVE");
129✔
1522

1523
    if (pszIndexMode)
129✔
1524
        option_to_index_type(pszIndexMode, poDS->eIndexMode);
107✔
1525

1526
    std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
258✔
1527

1528
    int iYDim = 0;
129✔
1529
    int iXDim = 1;
129✔
1530
    if ((dims.size() == 2) || (dims.size() == 3))
129✔
1531
    {
1532
        if (dims.size() == 3)
129✔
1533
        {
1534
            if ((pszAttr != nullptr) &&
141✔
1535
                (schema.attributes().count(pszAttr) == 0))
141✔
1536
            {
1537
                CPLError(CE_Failure, CPLE_NotSupported,
×
1538
                         "%s attribute is not found in TileDB schema.",
1539
                         pszAttr);
1540
                return nullptr;
×
1541
            }
1542

1543
            if (poDS->eIndexMode == PIXEL)
123✔
1544
                std::rotate(dims.begin(), dims.begin() + 2, dims.end());
15✔
1545

1546
            if (dims[0].type() != TILEDB_UINT64)
123✔
1547
            {
1548
                const char *pszTypeName = "";
×
1549
                tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
×
1550
                CPLError(CE_Failure, CPLE_NotSupported,
×
1551
                         "Unsupported BAND dimension type: %s", pszTypeName);
1552
                return nullptr;
×
1553
            }
1554
            poDS->nBandStart = dims[0].domain<uint64_t>().first;
123✔
1555
            const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
123✔
1556
            if (nBandEnd < poDS->nBandStart ||
246✔
1557
                nBandEnd - poDS->nBandStart >
123✔
1558
                    static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
123✔
1559
            {
1560
                CPLError(CE_Failure, CPLE_NotSupported,
×
1561
                         "Invalid bounds for BAND dimension.");
1562
                return nullptr;
×
1563
            }
1564
            poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
123✔
1565
            iYDim = 1;
123✔
1566
            iXDim = 2;
123✔
1567
        }
1568
        else
1569
        {
1570
            const char *pszBands =
1571
                poDS->GetMetadataItem("NUM_BANDS", "IMAGE_STRUCTURE");
6✔
1572
            if (pszBands)
6✔
1573
            {
1574
                poDS->nBands = atoi(pszBands);
1✔
1575
            }
1576

1577
            poDS->eIndexMode = ATTRIBUTES;
6✔
1578
        }
1579
    }
1580
    else
1581
    {
1582
        CPLError(CE_Failure, CPLE_AppDefined,
×
1583
                 "Wrong number of dimensions %d: expected 2 or 3.",
1584
                 static_cast<int>(dims.size()));
×
1585
        return nullptr;
×
1586
    }
1587

1588
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
258✔
1589
        !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
129✔
1590
    {
1591
        return nullptr;
×
1592
    }
1593

1594
    if (dims[iYDim].type() != TILEDB_UINT64)
129✔
1595
    {
1596
        const char *pszTypeName = "";
×
1597
        tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
×
1598
        CPLError(CE_Failure, CPLE_NotSupported,
×
1599
                 "Unsupported Y dimension type: %s", pszTypeName);
1600
        return nullptr;
×
1601
    }
1602
    if (!pszYSize)
129✔
1603
    {
1604
        const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
16✔
1605
        const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
16✔
1606
        if (nStart != 0 ||
32✔
1607
            nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
16✔
1608
        {
1609
            CPLError(CE_Failure, CPLE_NotSupported,
×
1610
                     "Invalid bounds for Y dimension.");
1611
            return nullptr;
×
1612
        }
1613
        poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
16✔
1614
    }
1615
    const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
129✔
1616
    if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
129✔
1617
    {
1618
        CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
×
1619
        return nullptr;
×
1620
    }
1621
    poDS->nBlockYSize = static_cast<int>(nBlockYSize);
129✔
1622

1623
    if (dims[iXDim].type() != TILEDB_UINT64)
129✔
1624
    {
1625
        const char *pszTypeName = "";
×
1626
        tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
×
1627
        CPLError(CE_Failure, CPLE_NotSupported,
×
1628
                 "Unsupported Y dimension type: %s", pszTypeName);
1629
        return nullptr;
×
1630
    }
1631
    if (!pszXSize)
129✔
1632
    {
1633
        const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
16✔
1634
        const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
16✔
1635
        if (nStart != 0 ||
32✔
1636
            nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
16✔
1637
        {
1638
            CPLError(CE_Failure, CPLE_NotSupported,
×
1639
                     "Invalid bounds for X dimension.");
1640
            return nullptr;
×
1641
        }
1642
        poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
16✔
1643
    }
1644
    const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
129✔
1645
    if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
129✔
1646
    {
1647
        CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
×
1648
        return nullptr;
×
1649
    }
1650
    poDS->nBlockXSize = static_cast<int>(nBlockXSize);
129✔
1651

1652
    poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
129✔
1653
    poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
129✔
1654

1655
    if (dims.size() == 3)
129✔
1656
    {
1657
        // Create band information objects.
1658
        for (int i = 1; i <= poDS->nBands; ++i)
844✔
1659
        {
1660
            if (pszAttr == nullptr)
721✔
1661
                poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
189✔
1662
            else
1663
                poDS->SetBand(
1,064✔
1664
                    i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
1,064✔
1665
        }
1666
    }
1667
    else  // subdatasets or only attributes
1668
    {
1669
        if ((poOpenInfo->eAccess == GA_Update) &&
7✔
1670
            (poDS->GetMetadata("SUBDATASETS") != nullptr))
1✔
1671
        {
1672
            CPLError(CE_Failure, CPLE_NotSupported,
×
1673
                     "The TileDB driver does not support update access "
1674
                     "to subdatasets.");
1675
            return nullptr;
×
1676
        }
1677

1678
        if (!osSubdataset.empty())
6✔
1679
        {
1680
            // do we have the attribute in the schema
1681
            if (schema.attributes().count(osSubdataset))
2✔
1682
            {
1683
                poDS->SetBand(
×
1684
                    1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
×
1685
            }
1686
            else
1687
            {
1688
                if (schema.attributes().count(osSubdataset + "_1"))
2✔
1689
                {
1690
                    // Create band information objects.
1691
                    for (int i = 1; i <= poDS->nBands; ++i)
2✔
1692
                    {
1693
                        CPLString osAttr = CPLString().Printf(
1✔
1694
                            "%s_%d", osSubdataset.c_str(), i);
2✔
1695
                        poDS->SetBand(
2✔
1696
                            i, new TileDBRasterBand(poDS.get(), i, osAttr));
1✔
1697
                    }
1698
                }
1699
                else
1700
                {
1701
                    CPLError(CE_Failure, CPLE_NotSupported,
1✔
1702
                             "%s attribute is not found in TileDB schema.",
1703
                             osSubdataset.c_str());
1704
                    return nullptr;
1✔
1705
                }
1706
            }
1707
        }
1708
        else
1709
        {
1710
            char **papszMeta = poDS->GetMetadata("SUBDATASETS");
4✔
1711
            if (papszMeta != nullptr)
4✔
1712
            {
1713
                if ((CSLCount(papszMeta) / 2) == 1)
1✔
1714
                {
1715
                    const char *pszSubDSName =
1716
                        poDS->m_aosSubdatasetMD.FetchNameValueDef(
×
1717
                            "SUBDATASET_1_NAME", "");
1718
                    return GDALDataset::FromHandle(
×
1719
                        GDALOpen(pszSubDSName, poOpenInfo->eAccess));
×
1720
                }
1721
            }
1722
            else if (poDS->eIndexMode == ATTRIBUTES)
3✔
1723
            {
1724
                poDS->nBands = schema.attribute_num();
3✔
1725

1726
                // Create band information objects.
1727
                for (int i = 1; i <= poDS->nBands; ++i)
11✔
1728
                {
1729
                    CPLString osAttr =
1730
                        TILEDB_VALUES + CPLString().Printf("_%i", i);
24✔
1731
                    poDS->SetBand(i,
16✔
1732
                                  new TileDBRasterBand(poDS.get(), i, osAttr));
8✔
1733
                }
1734
            }
1735
            else
1736
            {
1737
                CPLError(CE_Failure, CPLE_NotSupported,
×
1738
                         "%s is missing required TileDB subdataset metadata.",
1739
                         osURI.c_str());
1740
                return nullptr;
×
1741
            }
1742
        }
1743
    }
1744

1745
    // reload metadata now that bands are created to populate band metadata
1746
    poDS->TryLoadCachedXML(nullptr, false);
128✔
1747

1748
    tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
256✔
1749

1750
    if (!STARTS_WITH_CI(osURI, "TILEDB:") && vfs.is_dir(osURI))
128✔
1751
        poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
128✔
1752
    else
1753
        CPLError(CE_Warning, CPLE_AppDefined,
×
1754
                 "Overviews not supported for network writes.");
1755

1756
    return poDS.release();
128✔
1757
}
1758

1759
/************************************************************************/
1760
/*                              CreateAttribute()                       */
1761
/************************************************************************/
1762

1763
template <class T, class NoDataT = T>
1764
static tiledb::Attribute CreateAttribute(tiledb::Context &ctx,
129✔
1765
                                         const std::string &osAttrName,
1766
                                         tiledb::FilterList &filterList,
1767
                                         bool bHasFillValue, double dfFillValue)
1768
{
1769
    auto attr = tiledb::Attribute::create<T>(ctx, osAttrName, filterList);
129✔
1770
    if (bHasFillValue && GDALIsValueInRange<NoDataT>(dfFillValue))
129✔
1771
    {
1772
        const auto nVal = static_cast<NoDataT>(dfFillValue);
30✔
1773
        if (dfFillValue == static_cast<double>(nVal))
30✔
1774
        {
1775
            if constexpr (sizeof(T) == sizeof(NoDataT))
1776
            {
1777
                attr.set_fill_value(&nVal, sizeof(nVal));
26✔
1778
            }
1779
            else
1780
            {
1781
                T aVal = {nVal, nVal};
4✔
1782
                attr.set_fill_value(&aVal, sizeof(aVal));
4✔
1783
            }
1784
        }
1785
    }
1786
    return attr;
129✔
1787
}
1788

1789
/************************************************************************/
1790
/*                              CreateAttribute()                       */
1791
/************************************************************************/
1792

1793
CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
123✔
1794
                                            const CPLString &osAttrName,
1795
                                            const int nSubRasterCount,
1796
                                            bool bHasFillValue,
1797
                                            double dfFillValue)
1798
{
1799
    try
1800
    {
1801
        for (int i = 0; i < nSubRasterCount; ++i)
252✔
1802
        {
1803
            CPLString osName(osAttrName);
129✔
1804
            // a few special cases
1805
            // remove any leading slashes or
1806
            // additional slashes as in the case of hdf5
1807
            if STARTS_WITH (osName, "//")
129✔
1808
            {
1809
                osName = osName.substr(2);
2✔
1810
            }
1811

1812
            osName.replaceAll("/", "_");
129✔
1813
            CPLString osPrettyName = osName;
129✔
1814

1815
            if ((eIndexMode == ATTRIBUTES) ||
129✔
1816
                ((m_bHasSubDatasets) && (nSubRasterCount > 1)))
115✔
1817
            {
1818
                osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
14✔
1819
            }
1820

1821
            switch (eType)
129✔
1822
            {
1823
                case GDT_Byte:
53✔
1824
                {
1825
                    m_schema->add_attribute(::CreateAttribute<unsigned char>(
159✔
1826
                        *m_ctx, osName, *m_filterList, bHasFillValue,
53✔
1827
                        dfFillValue));
106✔
1828
                    nBitsPerSample = 8;
53✔
1829
                    break;
53✔
1830
                }
1831
                case GDT_Int8:
4✔
1832
                {
1833
                    m_schema->add_attribute(
4✔
1834
                        ::CreateAttribute<int8_t>(*m_ctx, osName, *m_filterList,
8✔
1835
                                                  bHasFillValue, dfFillValue));
8✔
1836
                    nBitsPerSample = 8;
4✔
1837
                    break;
4✔
1838
                }
1839
                case GDT_UInt16:
5✔
1840
                {
1841
                    m_schema->add_attribute(::CreateAttribute<uint16_t>(
15✔
1842
                        *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1843
                        dfFillValue));
10✔
1844
                    nBitsPerSample = 16;
5✔
1845
                    break;
5✔
1846
                }
1847
                case GDT_UInt32:
5✔
1848
                {
1849
                    m_schema->add_attribute(::CreateAttribute<uint32_t>(
15✔
1850
                        *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1851
                        dfFillValue));
10✔
1852
                    nBitsPerSample = 32;
5✔
1853
                    break;
5✔
1854
                }
1855
                case GDT_UInt64:
4✔
1856
                {
1857
                    m_schema->add_attribute(::CreateAttribute<uint64_t>(
12✔
1858
                        *m_ctx, osName, *m_filterList, bHasFillValue,
4✔
1859
                        dfFillValue));
8✔
1860
                    nBitsPerSample = 64;
4✔
1861
                    break;
4✔
1862
                }
1863
                case GDT_Int16:
5✔
1864
                {
1865
                    m_schema->add_attribute(::CreateAttribute<int16_t>(
15✔
1866
                        *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1867
                        dfFillValue));
10✔
1868
                    nBitsPerSample = 16;
5✔
1869
                    break;
5✔
1870
                }
1871
                case GDT_Int32:
7✔
1872
                {
1873
                    m_schema->add_attribute(::CreateAttribute<int32_t>(
21✔
1874
                        *m_ctx, osName, *m_filterList, bHasFillValue,
7✔
1875
                        dfFillValue));
14✔
1876
                    nBitsPerSample = 32;
7✔
1877
                    break;
7✔
1878
                }
1879
                case GDT_Int64:
4✔
1880
                {
1881
                    m_schema->add_attribute(::CreateAttribute<int64_t>(
12✔
1882
                        *m_ctx, osName, *m_filterList, bHasFillValue,
4✔
1883
                        dfFillValue));
8✔
1884
                    nBitsPerSample = 64;
4✔
1885
                    break;
4✔
1886
                }
1887
                case GDT_Float32:
12✔
1888
                {
1889
                    m_schema->add_attribute(
12✔
1890
                        ::CreateAttribute<float>(*m_ctx, osName, *m_filterList,
24✔
1891
                                                 bHasFillValue, dfFillValue));
24✔
1892
                    nBitsPerSample = 32;
12✔
1893
                    break;
12✔
1894
                }
1895
                case GDT_Float64:
8✔
1896
                {
1897
                    m_schema->add_attribute(
8✔
1898
                        ::CreateAttribute<double>(*m_ctx, osName, *m_filterList,
16✔
1899
                                                  bHasFillValue, dfFillValue));
16✔
1900
                    nBitsPerSample = 64;
8✔
1901
                    break;
8✔
1902
                }
1903
                case GDT_CInt16:
5✔
1904
                {
1905
                    m_schema->add_attribute(
5✔
1906
                        ::CreateAttribute<int16_t[2], int16_t>(
10✔
1907
                            *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1908
                            dfFillValue));
10✔
1909
                    nBitsPerSample = 16;
5✔
1910
                    break;
5✔
1911
                }
1912
                case GDT_CInt32:
5✔
1913
                {
1914
                    m_schema->add_attribute(
5✔
1915
                        ::CreateAttribute<int32_t[2], int32_t>(
10✔
1916
                            *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1917
                            dfFillValue));
10✔
1918
                    nBitsPerSample = 32;
5✔
1919
                    break;
5✔
1920
                }
1921
                case GDT_CFloat32:
5✔
1922
                {
1923
                    m_schema->add_attribute(::CreateAttribute<float[2], float>(
15✔
1924
                        *m_ctx, osName, *m_filterList, bHasFillValue,
5✔
1925
                        dfFillValue));
10✔
1926
                    nBitsPerSample = 32;
5✔
1927
                    break;
5✔
1928
                }
1929
                case GDT_CFloat64:
7✔
1930
                {
1931
                    m_schema->add_attribute(
7✔
1932
                        ::CreateAttribute<double[2], double>(
14✔
1933
                            *m_ctx, osName, *m_filterList, bHasFillValue,
7✔
1934
                            dfFillValue));
14✔
1935
                    nBitsPerSample = 64;
7✔
1936
                    break;
7✔
1937
                }
1938
                default:
×
1939
                    return CE_Failure;
×
1940
            }
1941

1942
            if ((m_bHasSubDatasets) && (i == 0))
129✔
1943
            {
1944
                CPLString osDim;
8✔
1945
                switch (nSubRasterCount)
8✔
1946
                {
1947
                    case 2:
×
1948
                        osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
×
1949
                        break;
×
1950
                    default:
8✔
1951
                        osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
1952
                                     nRasterYSize);
8✔
1953
                        break;
8✔
1954
                }
1955

1956
                const int nSubDataCount = 1 + m_aosSubdatasetMD.size() / 2;
8✔
1957
                m_aosSubdatasetMD.SetNameValue(
1958
                    CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
16✔
1959
                    CPLString().Printf("%s", osPrettyName.c_str()));
24✔
1960

1961
                m_aosSubdatasetMD.SetNameValue(
1962
                    CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
16✔
1963
                    CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
8✔
1964
                                       osPrettyName.c_str(),
1965
                                       GDALGetDataTypeName(eType)));
16✔
1966

1967
                // add to PAM metadata
1968
                if (!m_poSubDatasetsTree)
8✔
1969
                {
1970
                    m_poSubDatasetsTree.reset(
3✔
1971
                        CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
1972
                }
1973

1974
                CPLXMLNode *psSubNode = CPLCreateXMLNode(
8✔
1975
                    m_poSubDatasetsTree.get(), CXT_Element, "Subdataset");
1976
                CPLAddXMLAttributeAndValue(psSubNode, "name",
8✔
1977
                                           osPrettyName.c_str());
1978

1979
                CPLXMLNode *psMetaNode = CPLCreateXMLNode(
8✔
1980
                    CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
1981
                    CXT_Element, "Metadata");
1982
                CPLAddXMLAttributeAndValue(psMetaNode, "domain",
8✔
1983
                                           "IMAGE_STRUCTURE");
1984

1985
                CPLAddXMLAttributeAndValue(
8✔
1986
                    CPLCreateXMLElementAndValue(
1987
                        psMetaNode, "MDI",
1988
                        CPLString().Printf("%d", nRasterXSize)),
16✔
1989
                    "KEY", "X_SIZE");
1990

1991
                CPLAddXMLAttributeAndValue(
8✔
1992
                    CPLCreateXMLElementAndValue(
1993
                        psMetaNode, "MDI",
1994
                        CPLString().Printf("%d", nRasterYSize)),
16✔
1995
                    "KEY", "Y_SIZE");
1996

1997
                CPLAddXMLAttributeAndValue(
8✔
1998
                    CPLCreateXMLElementAndValue(
1999
                        psMetaNode, "MDI",
2000
                        CPLString().Printf("%s", GDALGetDataTypeName(eType))),
16✔
2001
                    "KEY", "DATA_TYPE");
2002

2003
                if (m_lpoAttributeDS.size() > 0)
8✔
2004
                {
2005
                    CPLAddXMLAttributeAndValue(
6✔
2006
                        CPLCreateXMLElementAndValue(
2007
                            psMetaNode, "MDI",
2008
                            CPLString().Printf("%d", nBands)),
12✔
2009
                        "KEY", "NUM_BANDS");
2010
                }
2011
                else
2012
                {
2013
                    CPLAddXMLAttributeAndValue(
2✔
2014
                        CPLCreateXMLElementAndValue(
2015
                            psMetaNode, "MDI",
2016
                            CPLString().Printf("%d", nSubRasterCount)),
4✔
2017
                        "KEY", "NUM_BANDS");
2018
                }
2019

2020
                CPLAddXMLAttributeAndValue(
8✔
2021
                    CPLCreateXMLElementAndValue(
2022
                        psMetaNode, "MDI",
2023
                        CPLString().Printf("%d", nBitsPerSample)),
16✔
2024
                    "KEY", "NBITS");
2025
            }
2026
        }
2027
        return CE_None;
123✔
2028
    }
2029
    catch (const tiledb::TileDBError &e)
×
2030
    {
2031
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
2032
        return CE_Failure;
×
2033
    }
2034
}
2035

2036
/************************************************************************/
2037
/*                              SetBlockSize()                          */
2038
/************************************************************************/
2039

2040
void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
1✔
2041
                                       CPLStringList &aosOptions)
2042

2043
{
2044
    int nX = 0;
1✔
2045
    int nY = 0;
1✔
2046
    poBand->GetBlockSize(&nX, &nY);
1✔
2047

2048
    if (!aosOptions.FetchNameValue("BLOCKXSIZE"))
1✔
2049
    {
2050
        aosOptions.SetNameValue("BLOCKXSIZE", CPLString().Printf("%d", nX));
1✔
2051
    }
2052

2053
    if (!aosOptions.FetchNameValue("BLOCKYSIZE"))
1✔
2054
    {
2055
        aosOptions.SetNameValue("BLOCKYSIZE", CPLString().Printf("%d", nY));
1✔
2056
    }
2057
}
1✔
2058

2059
/************************************************************************/
2060
/*                              CreateLL()                              */
2061
/*                                                                      */
2062
/*      Shared functionality between TileDBDataset::Create() and        */
2063
/*      TileDBDataset::CreateCopy() for creating TileDB array based on  */
2064
/*      a set of options and a configuration.                           */
2065
/************************************************************************/
2066

2067
TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
120✔
2068
                                                   int nXSize, int nYSize,
2069
                                                   int nBandsIn,
2070
                                                   GDALDataType eType,
2071
                                                   CSLConstList papszOptions)
2072
{
2073
    try
2074
    {
2075
        if ((nXSize <= 0 && nYSize <= 0))
120✔
2076
        {
2077
            return nullptr;
×
2078
        }
2079

2080
        auto poDS = std::make_unique<TileDBRasterDataset>();
240✔
2081
        poDS->nRasterXSize = nXSize;
120✔
2082
        poDS->nRasterYSize = nYSize;
120✔
2083
        poDS->eDataType = eType;
120✔
2084
        poDS->nBands = nBandsIn;
120✔
2085
        poDS->eAccess = GA_Update;
120✔
2086

2087
        if (poDS->nBands == 0)  // subdatasets
120✔
2088
        {
2089
            poDS->eIndexMode = ATTRIBUTES;
1✔
2090
        }
2091
        else
2092
        {
2093
            const char *pszIndexMode =
2094
                CSLFetchNameValue(papszOptions, "INTERLEAVE");
119✔
2095

2096
            if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
119✔
2097
                return nullptr;
×
2098
        }
2099

2100
        const char *pszConfig =
2101
            CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
120✔
2102
        if (pszConfig != nullptr)
120✔
2103
        {
2104
            poDS->m_osConfigFilename = pszConfig;
×
2105
            tiledb::Config cfg(pszConfig);
×
2106
            poDS->m_ctx.reset(new tiledb::Context(cfg));
×
2107
        }
2108
        else
2109
        {
2110
            poDS->m_ctx.reset(new tiledb::Context());
120✔
2111
        }
2112

2113
        if (CPLTestBool(
120✔
2114
                CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "YES")))
2115
        {
2116
            poDS->m_bDatasetInGroup = true;
110✔
2117
            tiledb::create_group(*(poDS->m_ctx.get()), pszFilename);
116✔
2118

2119
            {
2120
                tiledb::Group group(*(poDS->m_ctx.get()), pszFilename,
107✔
2121
                                    TILEDB_WRITE);
428✔
2122
                group.put_metadata(
107✔
2123
                    DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
2124
                    static_cast<int>(strlen(RASTER_DATASET_TYPE)),
2125
                    RASTER_DATASET_TYPE);
2126

2127
                group.close();
107✔
2128
            }
2129

2130
            // Full resolution raster array
2131
            poDS->m_osArrayURI =
107✔
2132
                CPLFormFilenameSafe(pszFilename, "l_0", nullptr);
214✔
2133
        }
2134
        else
2135
        {
2136
            poDS->m_osArrayURI = pszFilename;
10✔
2137
        }
2138

2139
        const char *pszCompression =
2140
            CSLFetchNameValue(papszOptions, "COMPRESSION");
117✔
2141
        const char *pszCompressionLevel =
2142
            CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
117✔
2143

2144
        const char *pszBlockXSize =
2145
            CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
117✔
2146
        poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
117✔
2147
        const char *pszBlockYSize =
2148
            CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
117✔
2149
        poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
117✔
2150
        poDS->bStats = CSLFetchBoolean(papszOptions, "STATS", FALSE);
117✔
2151

2152
        const char *pszTimestamp =
2153
            CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
117✔
2154
        if (pszTimestamp != nullptr)
117✔
2155
            poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
2✔
2156

2157
        // set dimensions and attribute type for schema
2158
        poDS->m_schema.reset(
234✔
2159
            new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
117✔
2160
        poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
117✔
2161
        poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
117✔
2162

2163
        poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
117✔
2164

2165
        if (pszCompression != nullptr)
117✔
2166
        {
2167
            int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
×
2168
            if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
×
2169
                                         *(poDS->m_filterList.get()),
×
2170
                                         pszCompression, nLevel) == CE_None)
×
2171
            {
2172
                poDS->SetMetadataItem("COMPRESSION", pszCompression,
×
2173
                                      "IMAGE_STRUCTURE");
×
2174
                poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
×
2175
            }
2176
        }
2177

2178
        CPLString osAux = CPLGetBasenameSafe(pszFilename);
234✔
2179
        osAux += ".tdb";
117✔
2180

2181
        poDS->SetPhysicalFilename(
234✔
2182
            CPLFormFilenameSafe(pszFilename, osAux.c_str(), nullptr).c_str());
234✔
2183

2184
        // Initialize PAM information.
2185
        poDS->SetDescription(pszFilename);
117✔
2186

2187
        // Note the dimension bounds are inclusive and are expanded to the match
2188
        // the block size
2189
        poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
117✔
2190
        poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
117✔
2191

2192
        // register additional attributes to the pixel value, these will be
2193
        // be reported as subdatasets on future reads
2194
        CSLConstList papszAttributes =
2195
            CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
117✔
2196
        for (const char *pszAttribute : cpl::Iterate(papszAttributes))
121✔
2197
        {
2198
            // modeling additional attributes as subdatasets
2199
            poDS->m_bHasSubDatasets = true;
4✔
2200
            // check each attribute is a GDAL source
2201
            std::unique_ptr<GDALDataset> poAttrDS(
2202
                GDALDataset::Open(pszAttribute, GA_ReadOnly));
8✔
2203

2204
            if (poAttrDS != nullptr)
4✔
2205
            {
2206
                // check each is co-registered
2207
                // candidate band
2208
                int nAttrBands = poAttrDS->GetRasterCount();
4✔
2209
                if (nAttrBands > 0)
4✔
2210
                {
2211
                    GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
4✔
2212

2213
                    if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
4✔
2214
                        (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
8✔
2215
                        (poDS->nBands == nAttrBands))
4✔
2216
                    {
2217
                        // could check geotransform, but it is sufficient
2218
                        // that cartesian dimensions are equal
2219
                        poDS->m_lpoAttributeDS.push_back(std::move(poAttrDS));
4✔
2220
                    }
2221
                    else
2222
                    {
2223
                        CPLError(
×
2224
                            CE_Warning, CPLE_AppDefined,
2225
                            "Skipping %s as it has a different dimension\n",
2226
                            pszAttribute);
2227
                    }
2228
                }
2229
                else
2230
                {
2231
                    CPLError(CE_Warning, CPLE_AppDefined,
×
2232
                             "Skipping %s as it doesn't have any bands\n",
2233
                             pszAttribute);
2234
                }
2235
            }
2236
            else
2237
            {
2238
                CPLError(CE_Warning, CPLE_AppDefined,
×
2239
                         "Skipping %s, not recognized as a GDAL dataset\n",
2240
                         pszAttribute);
2241
            }
2242
        }
2243

2244
        return poDS.release();
117✔
2245
    }
2246
    catch (const tiledb::TileDBError &e)
3✔
2247
    {
2248
        CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
3✔
2249
        return nullptr;
3✔
2250
    }
2251
}
2252

2253
/************************************************************************/
2254
/*                          DeferredCreate()                            */
2255
/*                                                                      */
2256
/*  Create dimension, domains and attributes. and optionally the array  */
2257
/************************************************************************/
2258

2259
bool TileDBRasterDataset::DeferredCreate(bool bCreateArray)
120✔
2260
{
2261
    CPLAssert(!m_bDeferredCreateHasRun);
120✔
2262
    m_bDeferredCreateHasRun = true;
120✔
2263
    m_bDeferredCreateHasBeenSuccessful = false;
120✔
2264

2265
    try
2266
    {
2267
        // this driver enforces that all subdatasets are the same size
2268
        tiledb::Domain domain(*m_ctx);
240✔
2269

2270
        const uint64_t w = static_cast<uint64_t>(nBlocksX) * nBlockXSize - 1;
120✔
2271
        const uint64_t h = static_cast<uint64_t>(nBlocksY) * nBlockYSize - 1;
120✔
2272

2273
        auto d1 = tiledb::Dimension::create<uint64_t>(*m_ctx, "X", {0, w},
120✔
2274
                                                      uint64_t(nBlockXSize));
483✔
2275
        auto d2 = tiledb::Dimension::create<uint64_t>(*m_ctx, "Y", {0, h},
117✔
2276
                                                      uint64_t(nBlockYSize));
468✔
2277

2278
        {
2279
            CPLErr eErr;
2280
            // Only used for unit test purposes (to check ability of GDAL to read
2281
            // an arbitrary array)
2282
            const char *pszAttrName =
2283
                CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
117✔
2284
            if ((nBands == 0) || (eIndexMode == ATTRIBUTES))
117✔
2285
            {
2286
                eErr = AddDimensions(domain, pszAttrName, d2, d1, nullptr);
6✔
2287
            }
2288
            else
2289
            {
2290
                auto d3 = tiledb::Dimension::create<uint64_t>(
2291
                    *m_ctx, "BANDS", {1, uint64_t(nBands)}, 1);
222✔
2292
                eErr = AddDimensions(domain, pszAttrName, d2, d1, &d3);
111✔
2293
            }
2294
            if (eErr != CE_None)
117✔
2295
                return false;
×
2296
        }
2297

2298
        m_schema->set_domain(domain).set_order(
117✔
2299
            {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
117✔
2300

2301
        // register additional attributes to the pixel value, these will be
2302
        // be reported as subdatasets on future reads
2303
        for (const auto &poAttrDS : m_lpoAttributeDS)
121✔
2304
        {
2305
            const std::string osAttrName =
2306
                CPLGetBasenameSafe(poAttrDS->GetDescription());
4✔
2307
            GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
4✔
2308
            int bHasNoData = false;
4✔
2309
            const double dfNoData = poAttrBand->GetNoDataValue(&bHasNoData);
4✔
2310
            CreateAttribute(poAttrBand->GetRasterDataType(), osAttrName.c_str(),
4✔
2311
                            1, CPL_TO_BOOL(bHasNoData), dfNoData);
4✔
2312
        }
2313

2314
        if (bCreateArray)
117✔
2315
        {
2316
            CreateArray();
116✔
2317
        }
2318

2319
        m_bDeferredCreateHasBeenSuccessful = true;
117✔
2320
        return true;
117✔
2321
    }
2322
    catch (const tiledb::TileDBError &e)
3✔
2323
    {
2324
        CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
3✔
2325
        return false;
3✔
2326
    }
2327
}
2328

2329
/************************************************************************/
2330
/*                          CreateArray()                               */
2331
/************************************************************************/
2332

2333
void TileDBRasterDataset::CreateArray()
117✔
2334
{
2335
    tiledb::Array::create(m_osArrayURI, *m_schema);
117✔
2336

2337
    if (m_bDatasetInGroup)
117✔
2338
    {
2339
        tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
321✔
2340
        group.add_member(m_osArrayURI, false);
107✔
2341
        group.close();
107✔
2342
    }
2343

2344
    if (nTimestamp)
117✔
2345
        m_array.reset(new tiledb::Array(
4✔
2346
            *m_ctx, m_osArrayURI, TILEDB_WRITE,
2✔
2347
            tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
6✔
2348
    else
2349
        m_array.reset(new tiledb::Array(*m_ctx, m_osArrayURI, TILEDB_WRITE));
115✔
2350
}
117✔
2351

2352
/************************************************************************/
2353
/*                              CopySubDatasets()                       */
2354
/*                                                                      */
2355
/*      Copy SubDatasets from src to a TileDBRasterDataset              */
2356
/*                                                                      */
2357
/************************************************************************/
2358

2359
CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
1✔
2360
                                            TileDBRasterDataset *poDstDS,
2361
                                            GDALProgressFunc pfnProgress,
2362
                                            void *pProgressData)
2363

2364
{
2365
    try
2366
    {
2367
        std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
2✔
2368
        poDstDS->m_bHasSubDatasets = true;
1✔
2369
        CSLConstList papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
1✔
2370
        if (!papszSrcSubDatasets)
1✔
2371
            return CE_Failure;
×
2372
        const char *pszSubDSName =
2373
            CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
1✔
2374
        if (!pszSubDSName)
1✔
2375
            return CE_Failure;
×
2376

2377
        CPLStringList apszTokens(CSLTokenizeString2(
2378
            pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
2✔
2379
        // FIXME? this is tailored for HDF5-like subdataset names
2380
        // HDF5:foo.hdf5:attrname.
2381
        if (apszTokens.size() != 3)
1✔
2382
        {
2383
            CPLError(CE_Failure, CPLE_AppDefined,
×
2384
                     "Cannot guess attribute name in %s", pszSubDSName);
2385
            return CE_Failure;
×
2386
        }
2387

2388
        std::unique_ptr<GDALDataset> poSubDataset(
2389
            GDALDataset::Open(pszSubDSName));
2✔
2390
        if (poSubDataset.get() == nullptr ||
2✔
2391
            poSubDataset->GetRasterCount() == 0)
1✔
2392
        {
2393
            return CE_Failure;
×
2394
        }
2395

2396
        uint64_t nSubXSize = poSubDataset->GetRasterXSize();
1✔
2397
        uint64_t nSubYSize = poSubDataset->GetRasterYSize();
1✔
2398

2399
        const char *pszAttrName = apszTokens[2];
1✔
2400

2401
        auto poFirstSubDSBand = poSubDataset->GetRasterBand(1);
1✔
2402
        int bFirstSubDSBandHasNoData = FALSE;
1✔
2403
        const double dfFirstSubDSBandNoData =
2404
            poFirstSubDSBand->GetNoDataValue(&bFirstSubDSBandHasNoData);
1✔
2405
        poDstDS->CreateAttribute(poFirstSubDSBand->GetRasterDataType(),
1✔
2406
                                 pszAttrName, poSubDataset->GetRasterCount(),
2407
                                 CPL_TO_BOOL(bFirstSubDSBandHasNoData),
1✔
2408
                                 dfFirstSubDSBandNoData);
2409
        apoDatasets.push_back(std::move(poSubDataset));
1✔
2410

2411
        for (const auto &[pszKey, pszValue] :
8✔
2412
             cpl::IterateNameValue(papszSrcSubDatasets))
9✔
2413
        {
2414
            if (EQUAL(pszKey, "SUBDATASET_1_NAME") || !strstr(pszKey, "_NAME"))
4✔
2415
            {
2416
                continue;
3✔
2417
            }
2418
            pszSubDSName = pszValue;
1✔
2419
            apszTokens = CSLTokenizeString2(
2420
                pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
1✔
2421
            if (apszTokens.size() != 3)
1✔
2422
            {
2423
                CPLError(CE_Failure, CPLE_AppDefined,
×
2424
                         "Cannot guess attribute name in %s", pszSubDSName);
2425
                continue;
×
2426
            }
2427

2428
            std::unique_ptr<GDALDataset> poSubDS(
2429
                GDALDataset::Open(pszSubDSName));
2✔
2430
            if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
1✔
2431
            {
2432
                GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
1✔
2433
                int nBlockXSize, nBlockYSize;
2434
                poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1✔
2435

2436
                int bHasNoData = FALSE;
1✔
2437
                const double dfNoData = poBand->GetNoDataValue(&bHasNoData);
1✔
2438

2439
                if ((poSubDS->GetRasterXSize() !=
1✔
2440
                     static_cast<int>(nSubXSize)) ||
1✔
2441
                    (poSubDS->GetRasterYSize() !=
1✔
2442
                     static_cast<int>(nSubYSize)) ||
1✔
2443
                    (nBlockXSize != poDstDS->nBlockXSize) ||
3✔
2444
                    (nBlockYSize != poDstDS->nBlockYSize))
1✔
2445
                {
2446
                    CPLError(CE_Warning, CPLE_AppDefined,
×
2447
                             "Sub-datasets must have the same dimension,"
2448
                             " and block sizes, skipping %s",
2449
                             pszSubDSName);
2450
                }
2451
                else
2452
                {
2453
                    pszAttrName = apszTokens[2];
1✔
2454
                    poDstDS->CreateAttribute(
1✔
2455
                        poSubDS->GetRasterBand(1)->GetRasterDataType(),
2456
                        pszAttrName, poSubDS->GetRasterCount(),
2457
                        CPL_TO_BOOL(bHasNoData), dfNoData);
1✔
2458
                    apoDatasets.push_back(std::move(poSubDS));
1✔
2459
                }
2460
            }
2461
            else
2462
            {
2463
                CPLError(
×
2464
                    CE_Warning, CPLE_AppDefined,
2465
                    "Sub-datasets must be not null and contain data in bands,"
2466
                    "skipping %s\n",
2467
                    pszSubDSName);
2468
            }
2469
        }
2470

2471
        poDstDS->SetMetadata(poDstDS->m_aosSubdatasetMD.List(), "SUBDATASETS");
1✔
2472

2473
        poDstDS->CreateArray();
1✔
2474

2475
        /* --------------------------------------------------------  */
2476
        /*      Report preliminary (0) progress.                     */
2477
        /* --------------------------------------------------------- */
2478
        if (!pfnProgress(0.0, nullptr, pProgressData))
1✔
2479
        {
2480
            CPLError(CE_Failure, CPLE_UserInterrupt,
×
2481
                     "User terminated CreateCopy()");
2482
            return CE_Failure;
×
2483
        }
2484

2485
        // copy over subdatasets by block
2486
        tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
2✔
2487
        query.set_layout(TILEDB_GLOBAL_ORDER);
1✔
2488
        const uint64_t nTotalBlocks =
1✔
2489
            static_cast<uint64_t>(poDstDS->nBlocksX) * poDstDS->nBlocksY;
1✔
2490
        uint64_t nBlockCounter = 0;
1✔
2491

2492
        // row-major
2493
        for (int j = 0; j < poDstDS->nBlocksY; ++j)
6✔
2494
        {
2495
            for (int i = 0; i < poDstDS->nBlocksX; ++i)
55✔
2496
            {
2497
                std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
50✔
2498
                // have to write set all tiledb attributes on write
2499
                int iAttr = 0;
50✔
2500
                for (auto &poSubDS : apoDatasets)
150✔
2501
                {
2502
                    const GDALDataType eDT =
2503
                        poSubDS->GetRasterBand(1)->GetRasterDataType();
100✔
2504

2505
                    for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
200✔
2506
                    {
2507
                        const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
100✔
2508
                        const size_t nValues =
100✔
2509
                            static_cast<size_t>(poDstDS->nBlockXSize) *
100✔
2510
                            poDstDS->nBlockYSize;
100✔
2511
                        void *pBlock = VSI_MALLOC_VERBOSE(nDTSize * nValues);
100✔
2512
                        if (!pBlock)
100✔
2513
                            return CE_Failure;
×
2514
                        aBlocks.emplace_back(pBlock, &VSIFree);
100✔
2515
                        GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
100✔
2516
                        if (poBand->ReadBlock(i, j, pBlock) == CE_None)
100✔
2517
                        {
2518
                            SetBuffer(
100✔
2519
                                &query, eDT,
2520
                                poDstDS->m_schema->attribute(iAttr).name(),
200✔
2521
                                pBlock, nValues);
2522
                        }
2523
                        ++iAttr;
100✔
2524
                    }
2525
                }
2526

2527
                if (poDstDS->bStats)
50✔
2528
                    tiledb::Stats::enable();
×
2529

2530
                auto status = query.submit();
50✔
2531

2532
                if (poDstDS->bStats)
50✔
2533
                {
2534
                    tiledb::Stats::dump(stdout);
×
2535
                    tiledb::Stats::disable();
×
2536
                }
2537

2538
                if (status == tiledb::Query::Status::FAILED)
50✔
2539
                {
2540
                    return CE_Failure;
×
2541
                }
2542

2543
                ++nBlockCounter;
50✔
2544
                if (!pfnProgress(static_cast<double>(nBlockCounter) /
50✔
2545
                                     static_cast<double>(nTotalBlocks),
50✔
2546
                                 nullptr, pProgressData))
2547
                {
2548
                    CPLError(CE_Failure, CPLE_UserInterrupt,
×
2549
                             "User terminated CreateCopy()");
2550
                    return CE_Failure;
×
2551
                }
2552
            }
2553
        }
2554

2555
        query.finalize();
1✔
2556

2557
        return CE_None;
1✔
2558
    }
2559
    catch (const tiledb::TileDBError &e)
×
2560
    {
2561
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
2562
        return CE_Failure;
×
2563
    }
2564
}
2565

2566
/************************************************************************/
2567
/*                              Create()                                */
2568
/************************************************************************/
2569

2570
TileDBRasterDataset *TileDBRasterDataset::Create(const char *pszFilename,
119✔
2571
                                                 int nXSize, int nYSize,
2572
                                                 int nBandsIn,
2573
                                                 GDALDataType eType,
2574
                                                 char **papszOptions)
2575

2576
{
2577
    CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
238✔
2578

2579
    std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
2580
        osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
238✔
2581

2582
    if (!poDS)
119✔
2583
        return nullptr;
3✔
2584

2585
    const char *pszAttrName =
2586
        CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
116✔
2587
    for (int i = 0; i < poDS->nBands; i++)
314✔
2588
    {
2589
        if (poDS->eIndexMode == ATTRIBUTES)
198✔
2590
            poDS->SetBand(
24✔
2591
                i + 1, new TileDBRasterBand(
2592
                           poDS.get(), i + 1,
12✔
2593
                           TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
24✔
2594
        else
2595
            poDS->SetBand(i + 1,
558✔
2596
                          new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
372✔
2597
    }
2598

2599
    // TILEDB_WRITE_IMAGE_STRUCTURE=NO only used for unit test purposes (to
2600
    // check ability of GDAL to read an arbitrary array)
2601
    if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
116✔
2602
    {
2603
        CPLStringList aosImageStruct;
204✔
2604
        aosImageStruct.SetNameValue(
2605
            "NBITS", CPLString().Printf("%d", poDS->nBitsPerSample));
102✔
2606
        aosImageStruct.SetNameValue(
2607
            "DATA_TYPE",
2608
            CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
102✔
2609
        aosImageStruct.SetNameValue(
2610
            "X_SIZE", CPLString().Printf("%d", poDS->nRasterXSize));
102✔
2611
        aosImageStruct.SetNameValue(
2612
            "Y_SIZE", CPLString().Printf("%d", poDS->nRasterYSize));
102✔
2613
        aosImageStruct.SetNameValue("INTERLEAVE",
2614
                                    index_type_name(poDS->eIndexMode));
102✔
2615
        aosImageStruct.SetNameValue("DATASET_TYPE", RASTER_DATASET_TYPE);
102✔
2616

2617
        if (poDS->m_lpoAttributeDS.size() > 0)
102✔
2618
        {
2619
            int i = 0;
2✔
2620
            for (auto const &poAttrDS : poDS->m_lpoAttributeDS)
6✔
2621
            {
2622
                aosImageStruct.SetNameValue(
4✔
2623
                    CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
8✔
2624
                    CPLGetBasenameSafe(poAttrDS->GetDescription()).c_str());
12✔
2625
            }
2626
        }
2627
        poDS->SetMetadata(aosImageStruct.List(), "IMAGE_STRUCTURE");
102✔
2628
    }
2629

2630
    return poDS.release();
116✔
2631
}
2632

2633
/************************************************************************/
2634
/*                              CreateCopy()                            */
2635
/************************************************************************/
2636

2637
GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
52✔
2638
                                             GDALDataset *poSrcDS, int bStrict,
2639
                                             char **papszOptions,
2640
                                             GDALProgressFunc pfnProgress,
2641
                                             void *pProgressData)
2642

2643
{
2644
    CPLStringList aosOptions(CSLDuplicate(papszOptions));
104✔
2645
    CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
104✔
2646

2647
    std::unique_ptr<TileDBRasterDataset> poDstDS;
52✔
2648

2649
    if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
52✔
2650
    {
2651
        // TileDB schemas are fixed
2652
        CPLError(CE_Failure, CPLE_NotSupported,
×
2653
                 "TileDB driver does not support "
2654
                 "appending to an existing schema.");
2655
        return nullptr;
×
2656
    }
2657

2658
    char **papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
52✔
2659

2660
    if (papszSrcSubDatasets == nullptr)
52✔
2661
    {
2662
        const int nBands = poSrcDS->GetRasterCount();
51✔
2663

2664
        if (nBands > 0)
51✔
2665
        {
2666
            GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
51✔
2667
            GDALDataType eType = poBand->GetRasterDataType();
51✔
2668

2669
            for (int i = 2; i <= nBands; ++i)
85✔
2670
            {
2671
                if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
34✔
2672
                {
2673
                    CPLError(CE_Failure, CPLE_NotSupported,
×
2674
                             "TileDB driver does not support "
2675
                             "source dataset with different band data types.");
2676
                    return nullptr;
×
2677
                }
2678
            }
2679

2680
            poDstDS.reset(TileDBRasterDataset::Create(
51✔
2681
                osArrayPath, poSrcDS->GetRasterXSize(),
2682
                poSrcDS->GetRasterYSize(), nBands, eType, papszOptions));
2683

2684
            if (!poDstDS)
51✔
2685
            {
2686
                return nullptr;
3✔
2687
            }
2688

2689
            for (int i = 1; i <= nBands; ++i)
130✔
2690
            {
2691
                int bHasNoData = FALSE;
82✔
2692
                const double dfNoData =
2693
                    poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
82✔
2694
                if (bHasNoData)
82✔
2695
                    poDstDS->GetRasterBand(i)->SetNoDataValue(dfNoData);
3✔
2696
            }
2697

2698
            CPLErr eErr =
2699
                GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
48✔
2700
                                           pfnProgress, pProgressData);
2701

2702
            if (eErr != CE_None)
48✔
2703
            {
2704
                CPLError(eErr, CPLE_AppDefined,
×
2705
                         "Error copying raster to TileDB.");
2706
                return nullptr;
×
2707
            }
2708
        }
2709
        else
2710
        {
2711
            CPLError(CE_Failure, CPLE_NotSupported,
×
2712
                     "TileDB driver does not support "
2713
                     "source dataset with zero bands.");
2714
            return nullptr;
×
2715
        }
2716
    }
2717
    else
2718
    {
2719
        if (bStrict)
1✔
2720
        {
2721
            CPLError(CE_Failure, CPLE_NotSupported,
×
2722
                     "TileDB driver does not support copying "
2723
                     "subdatasets in strict mode.");
2724
            return nullptr;
×
2725
        }
2726

2727
        if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
2✔
2728
            CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
1✔
2729
        {
2730
            CPLError(CE_Failure, CPLE_NotSupported,
×
2731
                     "Changing block size is not supported when copying "
2732
                     "subdatasets.");
2733
            return nullptr;
×
2734
        }
2735

2736
        const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
1✔
2737
        const int nMaxFiles =
2738
            atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
1✔
2739

2740
        aosOptions.SetNameValue("CREATE_GROUP", "NO");
1✔
2741

2742
        if (nSubDatasetCount <= nMaxFiles)
1✔
2743
        {
2744
            const char *pszSource =
2745
                CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
1✔
2746
            if (pszSource)
1✔
2747
            {
2748
                std::unique_ptr<GDALDataset> poSubDataset(
2749
                    GDALDataset::Open(pszSource));
1✔
2750
                if (poSubDataset && poSubDataset->GetRasterCount() > 0)
1✔
2751
                {
2752
                    GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
1✔
2753

2754
                    TileDBRasterDataset::SetBlockSize(poBand, aosOptions);
1✔
2755
                    poDstDS.reset(TileDBRasterDataset::CreateLL(
1✔
2756
                        osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
2757
                        poBand->GetRasterDataType(), aosOptions.List()));
1✔
2758

2759
                    if (poDstDS)
1✔
2760
                    {
2761
                        if (!poDstDS->DeferredCreate(
1✔
2762
                                /* bCreateArray = */ false))
2763
                            return nullptr;
×
2764

2765
                        if (TileDBRasterDataset::CopySubDatasets(
1✔
2766
                                poSrcDS, poDstDS.get(), pfnProgress,
2767
                                pProgressData) != CE_None)
1✔
2768
                        {
2769
                            poDstDS.reset();
×
2770
                            CPLError(CE_Failure, CPLE_AppDefined,
×
2771
                                     "Unable to copy subdatasets.");
2772
                        }
2773
                    }
2774
                }
2775
            }
2776
        }
2777
        else
2778
        {
2779
            CPLError(CE_Failure, CPLE_AppDefined,
×
2780
                     "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
2781
        }
2782
    }
2783

2784
    // TODO Supporting mask bands is a possible future task
2785
    if (poDstDS != nullptr)
49✔
2786
    {
2787
        int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
49✔
2788
        poDstDS->CloneInfo(poSrcDS, nCloneFlags);
49✔
2789

2790
        if (poDstDS->FlushCache(false) != CE_None)
49✔
2791
        {
2792
            CPLError(CE_Failure, CPLE_AppDefined, "FlushCache() failed");
×
2793
            return nullptr;
×
2794
        }
2795

2796
        return poDstDS.release();
49✔
2797
    }
2798
    return nullptr;
×
2799
}
2800

2801
/************************************************************************/
2802
/*                            LoadOverviews()                           */
2803
/************************************************************************/
2804

2805
void TileDBRasterDataset::LoadOverviews()
28✔
2806
{
2807
    if (m_bLoadOverviewsDone)
28✔
2808
        return;
23✔
2809
    m_bLoadOverviewsDone = true;
5✔
2810

2811
    // NOTE: read overview_model.rst for a high level explanation of overviews
2812
    // are stored.
2813

2814
    if (!m_bDatasetInGroup)
5✔
2815
        return;
×
2816

2817
    CPLStringList aosOpenOptions;
5✔
2818
    if (nTimestamp)
5✔
2819
    {
2820
        aosOpenOptions.SetNameValue("TILEDB_TIMESTAMP",
2821
                                    CPLSPrintf("%" PRIu64, nTimestamp));
×
2822
    }
2823
    if (!m_osConfigFilename.empty())
5✔
2824
    {
2825
        aosOpenOptions.SetNameValue("TILEDB_CONFIG",
2826
                                    m_osConfigFilename.c_str());
×
2827
    }
2828
    for (int i = 0; i < m_nOverviewCountFromMetadata; ++i)
10✔
2829
    {
2830
        const std::string osArrayName = CPLSPrintf("l_%d", 1 + i);
5✔
2831
        const std::string osOvrDatasetName =
2832
            CPLFormFilenameSafe(GetDescription(), osArrayName.c_str(), nullptr);
5✔
2833

2834
        GDALOpenInfo oOpenInfo(osOvrDatasetName.c_str(), eAccess);
5✔
2835
        oOpenInfo.papszOpenOptions = aosOpenOptions.List();
5✔
2836
        auto poOvrDS = std::unique_ptr<GDALDataset>(
2837
            Open(&oOpenInfo, tiledb::Object::Type::Array));
5✔
2838
        if (!poOvrDS)
5✔
2839
            return;
×
2840
        if (poOvrDS->GetRasterCount() != nBands)
5✔
2841
        {
2842
            CPLError(CE_Failure, CPLE_AppDefined,
×
2843
                     "Overview %s has not the same number of bands as full "
2844
                     "resolution dataset",
2845
                     osOvrDatasetName.c_str());
2846
            return;
×
2847
        }
2848
        m_apoOverviewDS.emplace_back(std::move(poOvrDS));
5✔
2849
    }
2850
}
2851

2852
/************************************************************************/
2853
/*                            IBuildOverviews()                         */
2854
/************************************************************************/
2855

2856
CPLErr TileDBRasterDataset::IBuildOverviews(
13✔
2857
    const char *pszResampling, int nOverviews, const int *panOverviewList,
2858
    int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
2859
    void *pProgressData, CSLConstList papszOptions)
2860
{
2861
    // NOTE: read overview_model.rst for a high level explanation of overviews
2862
    // are stored.
2863

2864
    if (eAccess == GA_ReadOnly)
13✔
2865
    {
2866
        if (!CPLTestBool(
3✔
2867
                CPLGetConfigOption("TILEDB_GEOTIFF_OVERVIEWS", "FALSE")))
2868
        {
2869
            ReportError(
2✔
2870
                CE_Failure, CPLE_NotSupported,
2871
                "Cannot %s overviews in TileDB format in read-only mode",
2872
                nOverviews ? "create" : "delete");
2873
            return CE_Failure;
2✔
2874
        }
2875

2876
        // GeoTIFF overviews. This used to be supported before GDAL 3.10
2877
        // although likely not desirable.
2878
        return GDALPamDataset::IBuildOverviews(
1✔
2879
            pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2880
            pfnProgress, pProgressData, papszOptions);
1✔
2881
    }
2882

2883
    if (nBands == 0)
10✔
2884
    {
2885
        return CE_Failure;
×
2886
    }
2887

2888
    // If we already have PAM overview (i.e. GeoTIFF based), go through PAM
2889
    if (cpl::down_cast<GDALPamRasterBand *>(GetRasterBand(1))
10✔
2890
            ->GDALPamRasterBand::GetOverviewCount() > 0)
10✔
2891
    {
2892
        return GDALPamDataset::IBuildOverviews(
1✔
2893
            pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2894
            pfnProgress, pProgressData, papszOptions);
1✔
2895
    }
2896

2897
    if (!m_bDatasetInGroup)
9✔
2898
    {
2899
        ReportError(CE_Failure, CPLE_NotSupported,
2✔
2900
                    "IBuildOverviews() only supported for datasets created "
2901
                    "with CREATE_GROUP=YES");
2902
        return CE_Failure;
2✔
2903
    }
2904

2905
    /* -------------------------------------------------------------------- */
2906
    /*      Our overview support currently only works safely if all         */
2907
    /*      bands are handled at the same time.                             */
2908
    /* -------------------------------------------------------------------- */
2909
    if (nListBands != nBands)
7✔
2910
    {
2911
        ReportError(CE_Failure, CPLE_NotSupported,
×
2912
                    "Generation of TileDB overviews currently only "
2913
                    "supported when operating on all bands.  "
2914
                    "Operation failed.");
2915
        return CE_Failure;
×
2916
    }
2917

2918
    // Force loading existing overviews
2919
    if (m_nOverviewCountFromMetadata)
7✔
2920
        GetRasterBand(1)->GetOverviewCount();
4✔
2921
    m_bLoadOverviewsDone = true;
7✔
2922

2923
    /* -------------------------------------------------------------------- */
2924
    /*      Deletes existing overviews if requested.                        */
2925
    /* -------------------------------------------------------------------- */
2926
    if (nOverviews == 0)
7✔
2927
    {
2928
        CPLErr eErr = CE_None;
2✔
2929

2930
        // Unlink arrays from he group
2931
        try
2932
        {
2933
            tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
6✔
2934
            for (auto &&poODS : m_apoOverviewDS)
4✔
2935
            {
2936
                group.remove_member(poODS->GetDescription());
2✔
2937
            }
2938
            group.close();
2✔
2939
        }
2940
        catch (const tiledb::TileDBError &e)
×
2941
        {
2942
            eErr = CE_Failure;
×
2943
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
2944
        }
2945

2946
        tiledb::VFS vfs(*m_ctx, m_ctx->config());
2✔
2947

2948
        // Delete arrays
2949
        for (auto &&poODS : m_apoOverviewDS)
4✔
2950
        {
2951
            try
2952
            {
2953
                CPL_IGNORE_RET_VAL(poODS->Close());
2✔
2954
                tiledb::Array::delete_array(*m_ctx, poODS->GetDescription());
2✔
2955
                if (vfs.is_dir(poODS->GetDescription()))
2✔
2956
                {
2957
                    vfs.remove_dir(poODS->GetDescription());
2✔
2958
                }
2959
            }
2960
            catch (const tiledb::TileDBError &e)
×
2961
            {
2962
                eErr = CE_Failure;
×
2963
                CPLError(CE_Failure, CPLE_AppDefined,
×
2964
                         "Array::delete_array(%s) failed: %s",
2965
                         poODS->GetDescription(), e.what());
×
2966
            }
2967
            m_apoOverviewDSRemoved.emplace_back(std::move(poODS));
2✔
2968
        }
2969

2970
        m_apoOverviewDS.clear();
2✔
2971
        m_nOverviewCountFromMetadata = 0;
2✔
2972
        MarkPamDirty();
2✔
2973
        return eErr;
2✔
2974
    }
2975

2976
    /* -------------------------------------------------------------------- */
2977
    /*      Establish which of the overview levels we already have, and     */
2978
    /*      which are new.                                                  */
2979
    /* -------------------------------------------------------------------- */
2980
    std::vector<bool> abRequireNewOverview(nOverviews, true);
10✔
2981
    for (int i = 0; i < nOverviews; ++i)
10✔
2982
    {
2983
        const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
5✔
2984
        const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
5✔
2985

2986
        for (const auto &poODS : m_apoOverviewDS)
6✔
2987
        {
2988
            const int nOvFactor =
2989
                GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
2✔
2990
                                    poODS->GetRasterYSize(), GetRasterYSize());
2991

2992
            // If we already have a 1x1 overview and this new one would result
2993
            // in it too, then don't create it.
2994
            if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
2✔
2995
                nOXSize == 1 && nOYSize == 1)
2✔
2996
            {
2997
                abRequireNewOverview[i] = false;
×
2998
                break;
×
2999
            }
3000

3001
            if (nOvFactor == panOverviewList[i] ||
3✔
3002
                nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
1✔
3003
                                                GetRasterXSize(),
3004
                                                GetRasterYSize()))
3005
            {
3006
                abRequireNewOverview[i] = false;
1✔
3007
                break;
1✔
3008
            }
3009
        }
3010

3011
        if (abRequireNewOverview[i])
5✔
3012
        {
3013
            CPLStringList aosCreationOptions;
4✔
3014
            aosCreationOptions.SetNameValue("CREATE_GROUP", "NO");
4✔
3015
            aosCreationOptions.SetNameValue(
3016
                "NBITS", CPLString().Printf("%d", nBitsPerSample));
4✔
3017
            aosCreationOptions.SetNameValue("INTERLEAVE",
3018
                                            index_type_name(eIndexMode));
4✔
3019
            if (nTimestamp)
4✔
3020
            {
3021
                aosCreationOptions.SetNameValue(
3022
                    "TILEDB_TIMESTAMP", CPLSPrintf("%" PRIu64, nTimestamp));
×
3023
            }
3024
            if (!m_osConfigFilename.empty())
4✔
3025
            {
3026
                aosCreationOptions.SetNameValue("TILEDB_CONFIG",
3027
                                                m_osConfigFilename.c_str());
×
3028
            }
3029

3030
            const std::string osArrayName =
3031
                CPLSPrintf("l_%d", 1 + int(m_apoOverviewDS.size()));
4✔
3032
            const std::string osOvrDatasetName = CPLFormFilenameSafe(
3033
                GetDescription(), osArrayName.c_str(), nullptr);
4✔
3034

3035
            auto poOvrDS = std::unique_ptr<TileDBRasterDataset>(
3036
                Create(osOvrDatasetName.c_str(), nOXSize, nOYSize, nBands,
3037
                       GetRasterBand(1)->GetRasterDataType(),
3038
                       aosCreationOptions.List()));
4✔
3039
            if (!poOvrDS)
4✔
3040
                return CE_Failure;
×
3041

3042
            // Apply nodata from main dataset
3043
            for (int j = 0; j < nBands; ++j)
16✔
3044
            {
3045
                int bHasNoData = FALSE;
12✔
3046
                const double dfNoData =
3047
                    GetRasterBand(j + 1)->GetNoDataValue(&bHasNoData);
12✔
3048
                if (bHasNoData)
12✔
3049
                    poOvrDS->GetRasterBand(j + 1)->SetNoDataValue(dfNoData);
12✔
3050
            }
3051

3052
            // Apply georeferencing from main dataset
3053
            poOvrDS->SetSpatialRef(GetSpatialRef());
4✔
3054
            GDALGeoTransform gt;
4✔
3055
            if (GetGeoTransform(gt) == CE_None)
4✔
3056
            {
3057
                gt.Rescale(static_cast<double>(nRasterXSize) / nOXSize,
4✔
3058
                           static_cast<double>(nRasterYSize) / nOYSize);
4✔
3059
                poOvrDS->SetGeoTransform(gt);
4✔
3060
            }
3061

3062
            poOvrDS->DeferredCreate(/* bCreateArray = */ true);
4✔
3063

3064
            try
3065
            {
3066
                tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
12✔
3067
                group.add_member(osOvrDatasetName, false);
4✔
3068
                group.close();
4✔
3069
            }
3070
            catch (const tiledb::TileDBError &e)
×
3071
            {
3072
                CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
3073
            }
3074

3075
            m_apoOverviewDS.emplace_back(std::move(poOvrDS));
4✔
3076
        }
3077
    }
3078

3079
    m_nOverviewCountFromMetadata = static_cast<int>(m_apoOverviewDS.size());
5✔
3080
    MarkPamDirty();
5✔
3081

3082
    /* -------------------------------------------------------------------- */
3083
    /*      Refresh/generate overviews that are listed.                     */
3084
    /* -------------------------------------------------------------------- */
3085
    std::vector<GDALRasterBand *> apoSrcBands;
10✔
3086
    std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands;
10✔
3087
    CPLErr eErr = CE_None;
5✔
3088
    const auto osNormalizedResampling =
3089
        GDALGetNormalizedOvrResampling(pszResampling);
5✔
3090
    for (int iBand = 0; eErr == CE_None && iBand < nBands; iBand++)
20✔
3091
    {
3092
        apoSrcBands.push_back(GetRasterBand(iBand + 1));
15✔
3093
        std::vector<GDALRasterBand *> apoOverviewBands;
30✔
3094

3095
        std::vector<bool> abAlreadyUsedOverviewBand(m_apoOverviewDS.size(),
3096
                                                    false);
30✔
3097

3098
        for (int i = 0; i < nOverviews; i++)
30✔
3099
        {
3100
            bool bFound = false;
15✔
3101
            for (size_t j = 0; j < m_apoOverviewDS.size(); ++j)
18✔
3102
            {
3103
                if (!abAlreadyUsedOverviewBand[j])
18✔
3104
                {
3105
                    auto &poODS = m_apoOverviewDS[j];
18✔
3106
                    int nOvFactor = GDALComputeOvFactor(
18✔
3107
                        poODS->GetRasterXSize(), nRasterXSize,
3108
                        poODS->GetRasterYSize(), nRasterYSize);
3109

3110
                    if (nOvFactor == panOverviewList[i] ||
21✔
3111
                        nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3✔
3112
                                                        nRasterXSize,
3113
                                                        nRasterYSize))
3114
                    {
3115
                        abAlreadyUsedOverviewBand[j] = true;
15✔
3116
                        auto poOvrBand = poODS->GetRasterBand(iBand + 1);
15✔
3117
                        if (!osNormalizedResampling.empty())
15✔
3118
                        {
3119
                            // Store resampling method in band metadata, as it
3120
                            // can be used by the gdaladdo utilities to refresh
3121
                            // existing overviews with the method previously
3122
                            // used
3123
                            poOvrBand->SetMetadataItem(
15✔
3124
                                "RESAMPLING", osNormalizedResampling.c_str());
15✔
3125
                        }
3126
                        apoOverviewBands.push_back(poOvrBand);
15✔
3127
                        bFound = true;
15✔
3128
                        break;
15✔
3129
                    }
3130
                }
3131
            }
3132
            if (!bFound)
15✔
3133
            {
3134
                CPLError(CE_Failure, CPLE_AppDefined,
×
3135
                         "Could not find dataset corresponding to ov factor %d",
3136
                         panOverviewList[i]);
×
3137
                eErr = CE_Failure;
×
3138
            }
3139
        }
3140
        if (iBand > 0)
15✔
3141
        {
3142
            CPLAssert(apoOverviewBands.size() == aapoOverviewBands[0].size());
10✔
3143
        }
3144
        aapoOverviewBands.emplace_back(std::move(apoOverviewBands));
15✔
3145
    }
3146

3147
    if (eErr == CE_None)
5✔
3148
    {
3149
        eErr = GDALRegenerateOverviewsMultiBand(apoSrcBands, aapoOverviewBands,
5✔
3150
                                                pszResampling, pfnProgress,
3151
                                                pProgressData, papszOptions);
3152
    }
3153

3154
    return eErr;
5✔
3155
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc