LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbdense.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1209 1476 81.9 %
Date: 2025-06-28 21:28:23 Functions: 57 59 96.6 %

          Line data    Source code
       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         226 : static CPLErr option_to_index_type(const char *pszIndexingType,
      61             :                                    TILEDB_INTERLEAVE_MODE &eMode)
      62             : {
      63         226 :     CPLErr eErr = CE_None;
      64             : 
      65         226 :     if (pszIndexingType)
      66             :     {
      67         139 :         if EQUAL (pszIndexingType, "BAND")
      68         108 :             eMode = BAND;
      69          31 :         else if EQUAL (pszIndexingType, "ATTRIBUTES")
      70           8 :             eMode = ATTRIBUTES;
      71          23 :         else if EQUAL (pszIndexingType, "PIXEL")
      72          23 :             eMode = PIXEL;
      73             :         else
      74             :         {
      75           0 :             eErr = CE_Failure;
      76           0 :             CPLError(eErr, CPLE_AppDefined,
      77             :                      "Unable to identify TileDB index mode %s.",
      78             :                      pszIndexingType);
      79             :         }
      80             :     }
      81             :     else
      82             :     {
      83          87 :         eMode = BAND;
      84             :     }
      85             : 
      86         226 :     return eErr;
      87             : }
      88             : 
      89         106 : static const char *index_type_name(TILEDB_INTERLEAVE_MODE eMode)
      90             : {
      91         106 :     switch (eMode)
      92             :     {
      93           8 :         case PIXEL:
      94           8 :             return "PIXEL";
      95           5 :         case ATTRIBUTES:
      96           5 :             return "ATTRIBUTES";
      97          93 :         case BAND:
      98          93 :             return "BAND";
      99           0 :         default:
     100           0 :             return nullptr;
     101             :     }
     102             : }
     103             : 
     104             : /************************************************************************/
     105             : /*                             SetBuffer()                              */
     106             : /************************************************************************/
     107             : 
     108         402 : static CPLErr SetBuffer(tiledb::Query *poQuery, GDALDataType eType,
     109             :                         const CPLString &osAttrName, void *pImage, size_t nSize)
     110             : {
     111         402 :     switch (eType)
     112             :     {
     113         200 :         case GDT_Byte:
     114             :             poQuery->set_data_buffer(
     115         200 :                 osAttrName, reinterpret_cast<unsigned char *>(pImage), nSize);
     116         200 :             break;
     117           2 :         case GDT_Int8:
     118             :             poQuery->set_data_buffer(osAttrName,
     119           2 :                                      reinterpret_cast<int8_t *>(pImage), nSize);
     120           2 :             break;
     121           3 :         case GDT_UInt16:
     122             :             poQuery->set_data_buffer(
     123           3 :                 osAttrName, reinterpret_cast<unsigned short *>(pImage), nSize);
     124           3 :             break;
     125           3 :         case GDT_UInt32:
     126             :             poQuery->set_data_buffer(
     127           3 :                 osAttrName, reinterpret_cast<unsigned int *>(pImage), nSize);
     128           3 :             break;
     129           2 :         case GDT_UInt64:
     130             :             poQuery->set_data_buffer(
     131           2 :                 osAttrName, reinterpret_cast<uint64_t *>(pImage), nSize);
     132           2 :             break;
     133           3 :         case GDT_Int16:
     134             :             poQuery->set_data_buffer(osAttrName,
     135           3 :                                      reinterpret_cast<short *>(pImage), nSize);
     136           3 :             break;
     137           9 :         case GDT_Int32:
     138             :             poQuery->set_data_buffer(osAttrName,
     139           9 :                                      reinterpret_cast<int *>(pImage), nSize);
     140           9 :             break;
     141           2 :         case GDT_Int64:
     142             :             poQuery->set_data_buffer(
     143           2 :                 osAttrName, reinterpret_cast<int64_t *>(pImage), nSize);
     144           2 :             break;
     145         159 :         case GDT_Float32:
     146             :             poQuery->set_data_buffer(osAttrName,
     147         159 :                                      reinterpret_cast<float *>(pImage), nSize);
     148         159 :             break;
     149           3 :         case GDT_Float64:
     150             :             poQuery->set_data_buffer(osAttrName,
     151           3 :                                      reinterpret_cast<double *>(pImage), nSize);
     152           3 :             break;
     153           3 :         case GDT_CInt16:
     154             :             poQuery->set_data_buffer(
     155           3 :                 osAttrName, reinterpret_cast<short *>(pImage), nSize * 2);
     156           3 :             break;
     157           3 :         case GDT_CInt32:
     158             :             poQuery->set_data_buffer(
     159           3 :                 osAttrName, reinterpret_cast<int *>(pImage), nSize * 2);
     160           3 :             break;
     161           3 :         case GDT_CFloat32:
     162             :             poQuery->set_data_buffer(
     163           3 :                 osAttrName, reinterpret_cast<float *>(pImage), nSize * 2);
     164           3 :             break;
     165           7 :         case GDT_CFloat64:
     166             :             poQuery->set_data_buffer(
     167           7 :                 osAttrName, reinterpret_cast<double *>(pImage), nSize * 2);
     168           7 :             break;
     169           0 :         default:
     170           0 :             return CE_Failure;
     171             :     }
     172         402 :     return CE_None;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                          TileDBRasterBand()                          */
     177             : /************************************************************************/
     178             : 
     179         928 : TileDBRasterBand::TileDBRasterBand(TileDBRasterDataset *poDSIn, int nBandIn,
     180         928 :                                    const std::string &osAttr)
     181         928 :     : poGDS(poDSIn), bStats(poDSIn->bStats), osAttrName(osAttr)
     182             : {
     183         928 :     poDS = poDSIn;
     184         928 :     nBand = nBandIn;
     185         928 :     eDataType = poGDS->eDataType;
     186         928 :     if (eDataType == GDT_Unknown)
     187             :     {
     188             :         try
     189             :         {
     190          14 :             auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
     191          14 :                             ->schema()
     192          28 :                             .attribute(osAttr);
     193          14 :             switch (attr.type())
     194             :             {
     195           1 :                 case TILEDB_INT8:
     196           1 :                     eDataType = GDT_Int8;
     197           1 :                     break;
     198           1 :                 case TILEDB_UINT8:
     199           1 :                     eDataType = GDT_Byte;
     200           1 :                     break;
     201           2 :                 case TILEDB_INT16:
     202           2 :                     eDataType =
     203           2 :                         attr.cell_val_num() == 2 ? GDT_CInt16 : GDT_Int16;
     204           2 :                     break;
     205           1 :                 case TILEDB_UINT16:
     206           1 :                     eDataType = GDT_UInt16;
     207           1 :                     break;
     208           2 :                 case TILEDB_INT32:
     209           2 :                     eDataType =
     210           2 :                         attr.cell_val_num() == 2 ? GDT_CInt32 : GDT_Int32;
     211           2 :                     break;
     212           1 :                 case TILEDB_UINT32:
     213           1 :                     eDataType = GDT_UInt32;
     214           1 :                     break;
     215           1 :                 case TILEDB_INT64:
     216           1 :                     eDataType = GDT_Int64;
     217           1 :                     break;
     218           1 :                 case TILEDB_UINT64:
     219           1 :                     eDataType = GDT_UInt64;
     220           1 :                     break;
     221           2 :                 case TILEDB_FLOAT32:
     222           2 :                     eDataType =
     223           2 :                         attr.cell_val_num() == 2 ? GDT_CFloat32 : GDT_Float32;
     224           2 :                     break;
     225           2 :                 case TILEDB_FLOAT64:
     226           2 :                     eDataType =
     227           2 :                         attr.cell_val_num() == 2 ? GDT_CFloat64 : GDT_Float64;
     228           2 :                     break;
     229           0 :                 default:
     230             :                 {
     231           0 :                     const char *pszTypeName = "";
     232           0 :                     tiledb_datatype_to_str(attr.type(), &pszTypeName);
     233           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     234             :                              "Unhandled TileDB data type: %s", pszTypeName);
     235           0 :                     break;
     236             :                 }
     237             :             }
     238             :         }
     239           0 :         catch (const tiledb::TileDBError &e)
     240             :         {
     241           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     242             :         }
     243             :     }
     244         928 :     eAccess = poGDS->eAccess;
     245         928 :     nRasterXSize = poGDS->nRasterXSize;
     246         928 :     nRasterYSize = poGDS->nRasterYSize;
     247         928 :     nBlockXSize = poGDS->nBlockXSize;
     248         928 :     nBlockYSize = poGDS->nBlockYSize;
     249         928 : }
     250             : 
     251             : /************************************************************************/
     252             : /*                             IRasterIO()                              */
     253             : /************************************************************************/
     254             : 
     255         344 : CPLErr TileDBRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     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         344 :     if (!poGDS->m_bDeferredCreateHasRun)
     263           5 :         poGDS->DeferredCreate(/* bCreateArray = */ true);
     264         344 :     if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
     265           0 :         return CE_Failure;
     266         344 :     if (!poGDS->m_array)
     267             :     {
     268           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
     269           1 :         return CE_Failure;
     270             :     }
     271             : 
     272         343 :     if (poGDS->eIndexMode == ATTRIBUTES && eRWFlag == GF_Write)
     273             :     {
     274           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     275             :                  "Unable to write using band ordered IRasterIO when using "
     276             :                  "interleave 'ATTRIBUTES'.\n");
     277           0 :         return CE_Failure;
     278             :     }
     279             : 
     280         343 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
     281             : 
     282         343 :     if (eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
     283         280 :         nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
     284         280 :         nLineSpace == nBufXSize * nPixelSpace)
     285             :     {
     286         280 :         const uint64_t nBandIdx = poGDS->nBandStart + nBand - 1;
     287             :         std::vector<uint64_t> oaSubarray = {
     288             :             nBandIdx,
     289             :             nBandIdx,
     290         280 :             static_cast<uint64_t>(nYOff),
     291         280 :             static_cast<uint64_t>(nYOff) + nYSize - 1,
     292         280 :             static_cast<uint64_t>(nXOff),
     293         560 :             static_cast<uint64_t>(nXOff) + nXSize - 1};
     294         280 :         if (poGDS->eIndexMode == PIXEL)
     295          84 :             std::rotate(oaSubarray.begin(), oaSubarray.begin() + 2,
     296          84 :                         oaSubarray.end());
     297             : 
     298             :         try
     299             :         {
     300         280 :             tiledb::Context *ctx = poGDS->m_ctx.get();
     301         280 :             const auto &oArray = poGDS->GetArray(eRWFlag == GF_Write, ctx);
     302             : 
     303         560 :             auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
     304         560 :             tiledb::Subarray subarray(*ctx, oArray);
     305         280 :             if (poGDS->m_array->schema().domain().ndim() == 3)
     306             :             {
     307         213 :                 subarray.set_subarray(oaSubarray);
     308             :             }
     309             :             else
     310             :             {
     311         268 :                 subarray.set_subarray(std::vector<uint64_t>(
     312         201 :                     oaSubarray.cbegin() + 2, oaSubarray.cend()));
     313             :             }
     314         280 :             poQuery->set_subarray(subarray);
     315             : 
     316         280 :             const size_t nValues = static_cast<size_t>(nBufXSize) * nBufYSize;
     317         280 :             SetBuffer(poQuery.get(), eDataType, osAttrName, pData, nValues);
     318             : 
     319             :             // write additional co-registered values
     320         560 :             std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
     321             : 
     322         280 :             if (poGDS->m_lpoAttributeDS.size() > 0)
     323             :             {
     324          12 :                 const int nXSizeToRead = nXOff + nXSize > nRasterXSize
     325           6 :                                              ? nRasterXSize - nXOff
     326             :                                              : nXSize;
     327          12 :                 const int nYSizeToRead = nYOff + nYSize > nRasterYSize
     328           6 :                                              ? nRasterYSize - nYOff
     329             :                                              : nYSize;
     330          18 :                 for (auto const &poAttrDS : poGDS->m_lpoAttributeDS)
     331             :                 {
     332          12 :                     GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(nBand);
     333          12 :                     GDALDataType eAttrType = poAttrBand->GetRasterDataType();
     334          12 :                     int nBytes = GDALGetDataTypeSizeBytes(eAttrType);
     335          12 :                     void *pAttrBlock = VSI_MALLOC_VERBOSE(nBytes * nValues);
     336             : 
     337          12 :                     if (pAttrBlock == nullptr)
     338             :                     {
     339           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory,
     340             :                                  "Cannot allocate attribute buffer");
     341           0 :                         return CE_Failure;
     342             :                     }
     343          12 :                     aBlocks.emplace_back(pAttrBlock, &VSIFree);
     344             : 
     345          12 :                     poAttrBand->AdviseRead(nXOff, nYOff, nXSizeToRead,
     346             :                                            nYSizeToRead, nXSizeToRead,
     347          12 :                                            nYSizeToRead, eAttrType, nullptr);
     348             : 
     349          12 :                     CPLErr eErr = poAttrBand->RasterIO(
     350             :                         GF_Read, nXOff, nYOff, nXSizeToRead, nYSizeToRead,
     351             :                         pAttrBlock, nXSizeToRead, nYSizeToRead, eAttrType,
     352             :                         nPixelSpace, nLineSpace, psExtraArg);
     353             : 
     354          12 :                     if (eErr == CE_None)
     355             :                     {
     356             :                         CPLString osName =
     357          24 :                             CPLGetBasenameSafe(poAttrDS->GetDescription());
     358             : 
     359          12 :                         SetBuffer(poQuery.get(), eAttrType, osName, pAttrBlock,
     360             :                                   nValues);
     361             :                     }
     362             :                     else
     363             :                     {
     364           0 :                         return eErr;
     365             :                     }
     366             :                 }
     367             :             }
     368             : 
     369         280 :             if (bStats)
     370           0 :                 tiledb::Stats::enable();
     371             : 
     372         280 :             auto status = poQuery->submit();
     373             : 
     374         280 :             if (bStats)
     375             :             {
     376           0 :                 tiledb::Stats::dump(stdout);
     377           0 :                 tiledb::Stats::disable();
     378             :             }
     379             : 
     380         280 :             if (status == tiledb::Query::Status::FAILED)
     381           0 :                 return CE_Failure;
     382             :             else
     383         280 :                 return CE_None;
     384             :         }
     385           0 :         catch (const tiledb::TileDBError &e)
     386             :         {
     387           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     388             :                      "TileDB: TileDBRasterBand::IRasterIO() failed: %s",
     389           0 :                      e.what());
     390           0 :             return CE_Failure;
     391             :         }
     392             :     }
     393             : 
     394          63 :     return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     395             :                                         pData, nBufXSize, nBufYSize, eBufType,
     396          63 :                                         nPixelSpace, nLineSpace, psExtraArg);
     397             : }
     398             : 
     399         140 : CPLErr TileDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     400             :                                     void *pImage)
     401             : {
     402         140 :     const int nXOff = nBlockXOff * nBlockXSize;
     403         140 :     const int nYOff = nBlockYOff * nBlockYSize;
     404         140 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     405         280 :     return IRasterIO(GF_Read, nXOff, nYOff, nBlockXSize, nBlockYSize, pImage,
     406             :                      nBlockXSize, nBlockYSize, eDataType, nDTSize,
     407         140 :                      nDTSize * nBlockXSize, nullptr);
     408             : }
     409             : 
     410             : /************************************************************************/
     411             : /*                             IWriteBlock()                            */
     412             : /************************************************************************/
     413             : 
     414          15 : CPLErr TileDBRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
     415             :                                      void *pImage)
     416             : 
     417             : {
     418          15 :     if (eAccess == GA_ReadOnly)
     419             :     {
     420           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     421             :                  "Unable to write block, dataset is opened read only.\n");
     422           0 :         return CE_Failure;
     423             :     }
     424             : 
     425          15 :     CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     426             :               pImage != nullptr);
     427             : 
     428          15 :     int nStartX = nBlockXSize * nBlockXOff;
     429          15 :     int nStartY = nBlockYSize * nBlockYOff;
     430             : 
     431          15 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     432          30 :     return IRasterIO(GF_Write, nStartX, nStartY, nBlockXSize, nBlockYSize,
     433             :                      pImage, nBlockXSize, nBlockYSize, eDataType, nDTSize,
     434          15 :                      nDTSize * nBlockXSize, nullptr);
     435             : }
     436             : 
     437             : /************************************************************************/
     438             : /*                       GetColorInterpretation()                       */
     439             : /************************************************************************/
     440             : 
     441          70 : GDALColorInterp TileDBRasterBand::GetColorInterpretation()
     442             : 
     443             : {
     444          70 :     if (poGDS->nBands == 1)
     445          20 :         return GCI_GrayIndex;
     446             : 
     447          50 :     if (nBand == 1)
     448          20 :         return GCI_RedBand;
     449             : 
     450          30 :     else if (nBand == 2)
     451          15 :         return GCI_GreenBand;
     452             : 
     453          15 :     else if (nBand == 3)
     454          15 :         return GCI_BlueBand;
     455             : 
     456           0 :     return GCI_AlphaBand;
     457             : }
     458             : 
     459             : /************************************************************************/
     460             : /*                           GetNoDataValue()                           */
     461             : /************************************************************************/
     462             : 
     463          79 : double TileDBRasterBand::GetNoDataValue(int *pbHasNoData)
     464             : {
     465          79 :     if (pbHasNoData)
     466          76 :         *pbHasNoData = false;
     467          79 :     if (m_bNoDataSet)
     468             :     {
     469          11 :         if (pbHasNoData)
     470          11 :             *pbHasNoData = true;
     471          11 :         return m_dfNoData;
     472             :     }
     473          68 :     if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
     474           0 :         return 0.0;
     475          68 :     if (!poGDS->m_array)
     476           0 :         return 0.0;
     477          68 :     double dfNoData = 0.0;
     478             :     try
     479             :     {
     480          68 :         const void *value = nullptr;
     481          68 :         uint64_t size = 0;
     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          68 :         auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
     486          68 :                         ->schema()
     487         136 :                         .attribute(osAttrName);
     488          68 :         attr.get_fill_value(&value, &size);
     489          68 :         if (value &&
     490          68 :             size == static_cast<uint64_t>(GDALGetDataTypeSizeBytes(eDataType)))
     491             :         {
     492          68 :             switch (eDataType)
     493             :             {
     494          51 :                 case GDT_Byte:
     495          51 :                     dfNoData = *static_cast<const uint8_t *>(value);
     496          51 :                     break;
     497           1 :                 case GDT_Int8:
     498           1 :                     dfNoData = *static_cast<const int8_t *>(value);
     499           1 :                     break;
     500           1 :                 case GDT_UInt16:
     501           1 :                     dfNoData = *static_cast<const uint16_t *>(value);
     502           1 :                     break;
     503           2 :                 case GDT_Int16:
     504             :                 case GDT_CInt16:
     505           2 :                     dfNoData = *static_cast<const int16_t *>(value);
     506           2 :                     break;
     507           1 :                 case GDT_UInt32:
     508           1 :                     dfNoData = *static_cast<const uint32_t *>(value);
     509           1 :                     break;
     510           2 :                 case GDT_Int32:
     511             :                 case GDT_CInt32:
     512           2 :                     dfNoData = *static_cast<const int32_t *>(value);
     513           2 :                     break;
     514           0 :                 case GDT_UInt64:
     515           0 :                     dfNoData = static_cast<double>(
     516           0 :                         *static_cast<const uint64_t *>(value));
     517           0 :                     break;
     518           0 :                 case GDT_Int64:
     519           0 :                     dfNoData = static_cast<double>(
     520           0 :                         *static_cast<const int64_t *>(value));
     521           0 :                     break;
     522           0 :                 case GDT_Float16:
     523             :                 case GDT_CFloat16:
     524             :                     // tileDB does not support float16
     525           0 :                     CPLAssert(false);
     526             :                     break;
     527           5 :                 case GDT_Float32:
     528             :                 case GDT_CFloat32:
     529           5 :                     dfNoData = *static_cast<const float *>(value);
     530           5 :                     break;
     531           5 :                 case GDT_Float64:
     532             :                 case GDT_CFloat64:
     533           5 :                     dfNoData = *static_cast<const double *>(value);
     534           5 :                     break;
     535           0 :                 case GDT_Unknown:
     536             :                 case GDT_TypeCount:
     537           0 :                     break;
     538             :             }
     539          68 :             if (pbHasNoData)
     540          65 :                 *pbHasNoData = true;
     541             :         }
     542             :     }
     543           0 :     catch (const tiledb::TileDBError &e)
     544             :     {
     545           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     546             :     }
     547          68 :     return dfNoData;
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                           IsValidNoData()                            */
     552             : /************************************************************************/
     553             : 
     554          37 : template <class T> static bool IsValidNoData(double dfNoData)
     555             : {
     556          72 :     return GDALIsValueInRange<T>(dfNoData) &&
     557          72 :            dfNoData == static_cast<double>(static_cast<T>(dfNoData));
     558             : }
     559             : 
     560             : /************************************************************************/
     561             : /*                           SetNoDataValue()                           */
     562             : /************************************************************************/
     563             : 
     564          45 : CPLErr TileDBRasterBand::SetNoDataValue(double dfNoData)
     565             : {
     566          45 :     if (poGDS->m_bDeferredCreateHasBeenSuccessful)
     567             :     {
     568           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     569             :                  "TileDBRasterBand::SetNoDataValue(): cannot be called after "
     570             :                  "pixel values have been set");
     571           1 :         return CE_Failure;
     572             :     }
     573             : 
     574          56 :     if (nBand != 1 &&
     575             :         dfNoData !=
     576          12 :             cpl::down_cast<TileDBRasterBand *>(poGDS->papoBands[0])->m_dfNoData)
     577             :     {
     578           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     579             :                  "TileDBRasterBand::SetNoDataValue(): all bands should have "
     580             :                  "the same nodata value");
     581           1 :         return CE_Failure;
     582             :     }
     583             : 
     584          43 :     bool bIsValid = false;
     585          43 :     switch (eDataType)
     586             :     {
     587          26 :         case GDT_Byte:
     588          26 :             bIsValid = IsValidNoData<uint8_t>(dfNoData);
     589          26 :             break;
     590           1 :         case GDT_Int8:
     591           1 :             bIsValid = IsValidNoData<int8_t>(dfNoData);
     592           1 :             break;
     593           1 :         case GDT_UInt16:
     594           1 :             bIsValid = IsValidNoData<uint16_t>(dfNoData);
     595           1 :             break;
     596           2 :         case GDT_Int16:
     597             :         case GDT_CInt16:
     598           2 :             bIsValid = IsValidNoData<int16_t>(dfNoData);
     599           2 :             break;
     600           1 :         case GDT_UInt32:
     601           1 :             bIsValid = IsValidNoData<uint32_t>(dfNoData);
     602           1 :             break;
     603           2 :         case GDT_Int32:
     604             :         case GDT_CInt32:
     605           2 :             bIsValid = IsValidNoData<int32_t>(dfNoData);
     606           2 :             break;
     607           0 :         case GDT_UInt64:
     608           0 :             bIsValid = IsValidNoData<uint64_t>(dfNoData);
     609           0 :             break;
     610           0 :         case GDT_Int64:
     611           0 :             bIsValid = IsValidNoData<int64_t>(dfNoData);
     612           0 :             break;
     613           0 :         case GDT_Float16:
     614             :         case GDT_CFloat16:
     615             :             // tileDB does not support float16
     616           0 :             bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
     617           0 :             break;
     618           5 :         case GDT_Float32:
     619             :         case GDT_CFloat32:
     620           5 :             bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
     621           5 :             break;
     622           5 :         case GDT_Float64:
     623             :         case GDT_CFloat64:
     624           5 :             bIsValid = true;
     625           5 :             break;
     626           0 :         case GDT_Unknown:
     627             :         case GDT_TypeCount:
     628           0 :             break;
     629             :     }
     630          43 :     if (!bIsValid)
     631             :     {
     632           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     633             :                  "TileDBRasterBand::SetNoDataValue(): nodata value cannot be "
     634             :                  "stored in band data type");
     635           3 :         return CE_Failure;
     636             :     }
     637             : 
     638          40 :     m_dfNoData = dfNoData;
     639          40 :     m_bNoDataSet = true;
     640          40 :     return CE_None;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                           GetOverviewCount()                         */
     645             : /************************************************************************/
     646             : 
     647          15 : int TileDBRasterBand::GetOverviewCount()
     648             : {
     649          15 :     if (poGDS->m_nOverviewCountFromMetadata > 0)
     650             :     {
     651           7 :         poGDS->LoadOverviews();
     652           7 :         return static_cast<int>(poGDS->m_apoOverviewDS.size());
     653             :     }
     654           8 :     return GDALPamRasterBand::GetOverviewCount();
     655             : }
     656             : 
     657             : /************************************************************************/
     658             : /*                              GetOverview()                           */
     659             : /************************************************************************/
     660             : 
     661          22 : GDALRasterBand *TileDBRasterBand::GetOverview(int nIdx)
     662             : {
     663          22 :     if (poGDS->m_nOverviewCountFromMetadata > 0)
     664             :     {
     665          21 :         poGDS->LoadOverviews();
     666          21 :         if (nIdx >= 0 && nIdx < static_cast<int>(poGDS->m_apoOverviewDS.size()))
     667             :         {
     668          17 :             return poGDS->m_apoOverviewDS[nIdx]->GetRasterBand(nBand);
     669             :         }
     670           4 :         return nullptr;
     671             :     }
     672           1 :     return GDALPamRasterBand::GetOverview(nIdx);
     673             : }
     674             : 
     675             : /************************************************************************/
     676             : /* ==================================================================== */
     677             : /*                     TileDBRasterDataset                              */
     678             : /* ==================================================================== */
     679             : /************************************************************************/
     680             : 
     681             : /************************************************************************/
     682             : /*                     ~TileDBRasterDataset()                           */
     683             : /************************************************************************/
     684             : 
     685         498 : TileDBRasterDataset::~TileDBRasterDataset()
     686             : 
     687             : {
     688         249 :     TileDBRasterDataset::CloseDependentDatasets();
     689         249 :     TileDBRasterDataset::Close();
     690         498 : }
     691             : 
     692             : /************************************************************************/
     693             : /*                              GetArray()                              */
     694             : /************************************************************************/
     695             : 
     696         307 : tiledb::Array &TileDBRasterDataset::GetArray(bool bForWrite,
     697             :                                              tiledb::Context *&ctx)
     698             : {
     699         307 :     if (eAccess == GA_Update && !bForWrite)
     700             :     {
     701         105 :         if (!m_roArray)
     702             :         {
     703          34 :             m_roCtx = std::make_unique<tiledb::Context>(m_ctx->config());
     704          34 :             if (nTimestamp)
     705             :             {
     706          16 :                 m_roArray = std::make_unique<tiledb::Array>(
     707           8 :                     *m_roCtx, m_osArrayURI, TILEDB_READ,
     708          24 :                     tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
     709             :             }
     710             :             else
     711             :             {
     712          52 :                 m_roArray = std::make_unique<tiledb::Array>(
     713          78 :                     *m_roCtx, m_osArrayURI, TILEDB_READ);
     714             :             }
     715             :         }
     716             : 
     717         105 :         ctx = m_roCtx.get();
     718         105 :         return *(m_roArray.get());
     719             :     }
     720             : 
     721         202 :     ctx = m_ctx.get();
     722         202 :     return *(m_array.get());
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                           IRasterio()                                */
     727             : /************************************************************************/
     728             : 
     729          85 : CPLErr TileDBRasterDataset::IRasterIO(
     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          85 :     if (!m_bDeferredCreateHasRun)
     737          48 :         DeferredCreate(/* bCreateArray = */ true);
     738          85 :     if (!m_bDeferredCreateHasBeenSuccessful)
     739           0 :         return CE_Failure;
     740          85 :     if (!m_array)
     741             :     {
     742           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
     743           1 :         return CE_Failure;
     744             :     }
     745             : 
     746             :     // support special case of writing attributes for bands, all attributes have to be set at once
     747          84 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
     748             : 
     749          84 :     if (eIndexMode == ATTRIBUTES && nBandCount == nBands &&
     750           4 :         eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
     751           4 :         nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
     752           4 :         nLineSpace == nBufXSize * nPixelSpace &&
     753           1 :         ((nBandSpace == 0 && nBandCount == 1) ||
     754           3 :          nBandSpace == nBufYSize * nLineSpace))
     755             :     {
     756             :         std::vector<uint64_t> oaSubarray = {
     757           4 :             static_cast<uint64_t>(nYOff),
     758           4 :             static_cast<uint64_t>(nYOff) + nYSize - 1,
     759           4 :             static_cast<uint64_t>(nXOff),
     760           8 :             static_cast<uint64_t>(nXOff) + nXSize - 1};
     761             : 
     762             :         try
     763             :         {
     764           4 :             tiledb::Context *ctx = m_ctx.get();
     765           4 :             const auto &oArray = GetArray(eRWFlag == GF_Write, ctx);
     766             : 
     767           8 :             auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
     768           8 :             tiledb::Subarray subarray(*ctx, oArray);
     769           4 :             subarray.set_subarray(oaSubarray);
     770           4 :             poQuery->set_subarray(subarray);
     771             : 
     772          14 :             for (int b = 0; b < nBandCount; b++)
     773             :             {
     774          10 :                 TileDBRasterBand *poBand = cpl::down_cast<TileDBRasterBand *>(
     775          10 :                     GetRasterBand(panBandMap[b]));
     776          10 :                 const size_t nRegionSize =
     777          10 :                     static_cast<size_t>(nBufXSize) * nBufYSize * nBufferDTSize;
     778          10 :                 SetBuffer(poQuery.get(), eDataType, poBand->osAttrName,
     779          10 :                           static_cast<GByte *>(pData) + b * nRegionSize,
     780             :                           nRegionSize);
     781             :             }
     782             : 
     783           4 :             if (bStats)
     784           0 :                 tiledb::Stats::enable();
     785             : 
     786           4 :             auto status = poQuery->submit();
     787             : 
     788           4 :             if (bStats)
     789             :             {
     790           0 :                 tiledb::Stats::dump(stdout);
     791           0 :                 tiledb::Stats::disable();
     792             :             }
     793             : 
     794           4 :             if (status == tiledb::Query::Status::FAILED)
     795           0 :                 return CE_Failure;
     796             :             else
     797           4 :                 return CE_None;
     798             :         }
     799           0 :         catch (const tiledb::TileDBError &e)
     800             :         {
     801           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     802             :                      "TileDB: TileDBRasterDataset::IRasterIO() failed: %s",
     803           0 :                      e.what());
     804           0 :             return CE_Failure;
     805             :         }
     806             :     }
     807             : 
     808          80 :     return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     809             :                                      pData, nBufXSize, nBufYSize, eBufType,
     810             :                                      nBandCount, panBandMap, nPixelSpace,
     811          80 :                                      nLineSpace, nBandSpace, psExtraArg);
     812             : }
     813             : 
     814             : /************************************************************************/
     815             : /*                             AddDimensions()                          */
     816             : /************************************************************************/
     817             : 
     818         117 : CPLErr TileDBRasterDataset::AddDimensions(tiledb::Domain &domain,
     819             :                                           const char *pszAttrName,
     820             :                                           tiledb::Dimension &y,
     821             :                                           tiledb::Dimension &x,
     822             :                                           tiledb::Dimension *poBands)
     823             : 
     824             : {
     825         117 :     CPLErr eErr = CE_None;
     826             : 
     827             :     const bool bHasFillValue =
     828         117 :         nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_bNoDataSet
     829         117 :                : false;
     830             :     const double dfFillValue =
     831         117 :         nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_dfNoData
     832         117 :                : 0.0;
     833             : 
     834         117 :     switch (eIndexMode)
     835             :     {
     836           6 :         case ATTRIBUTES:
     837           6 :             domain.add_dimensions(y, x);
     838           6 :             eErr = CreateAttribute(eDataType, pszAttrName, nBands,
     839             :                                    bHasFillValue, dfFillValue);
     840           6 :             break;
     841           8 :         case PIXEL:
     842           8 :             assert(poBands);
     843           8 :             domain.add_dimensions(y, x, *poBands);
     844           8 :             eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
     845             :                                    dfFillValue);
     846           8 :             break;
     847         103 :         default:  // BAND
     848         103 :             assert(poBands);
     849         103 :             domain.add_dimensions(*poBands, y, x);
     850         103 :             eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
     851             :                                    dfFillValue);
     852         103 :             break;
     853             :     }
     854             : 
     855         117 :     return eErr;
     856             : }
     857             : 
     858             : /************************************************************************/
     859             : /*                              Close()                                 */
     860             : /************************************************************************/
     861             : 
     862         487 : CPLErr TileDBRasterDataset::Close()
     863             : {
     864         487 :     CPLErr eErr = CE_None;
     865         487 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     866             :     {
     867         249 :         if (TileDBRasterDataset::FlushCache(true) != CE_None)
     868           0 :             eErr = CE_Failure;
     869             : 
     870         249 :         if (!m_bDeferredCreateHasRun)
     871          62 :             DeferredCreate(/* bCreateArray = */ true);
     872             : 
     873         249 :         if (nPamFlags & GPF_DIRTY)
     874         135 :             TrySaveXML();
     875             : 
     876             :         try
     877             :         {
     878         249 :             if (m_array)
     879             :             {
     880         246 :                 m_array->close();
     881         246 :                 m_array.reset();
     882             :             }
     883             :         }
     884           0 :         catch (const tiledb::TileDBError &e)
     885             :         {
     886           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     887             :         }
     888             :         try
     889             :         {
     890         249 :             if (m_roArray)
     891             :             {
     892          34 :                 m_roArray->close();
     893          34 :                 m_roArray.reset();
     894             :             }
     895             :         }
     896           0 :         catch (const tiledb::TileDBError &e)
     897             :         {
     898           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     899             :         }
     900             : 
     901         249 :         if (GDALPamDataset::Close() != CE_None)
     902           0 :             eErr = CE_Failure;
     903             :     }
     904         487 :     return eErr;
     905             : }
     906             : 
     907             : /************************************************************************/
     908             : /*                       CloseDependentDatasets()                       */
     909             : /************************************************************************/
     910             : 
     911         249 : int TileDBRasterDataset::CloseDependentDatasets()
     912             : {
     913         249 :     int bDroppedRef = GDALPamDataset::CloseDependentDatasets();
     914         249 :     if (!m_apoOverviewDS.empty())
     915           6 :         bDroppedRef = TRUE;
     916         249 :     m_apoOverviewDS.clear();
     917         249 :     return bDroppedRef;
     918             : }
     919             : 
     920             : /************************************************************************/
     921             : /*                             FlushCache()                             */
     922             : /************************************************************************/
     923             : 
     924         315 : CPLErr TileDBRasterDataset::FlushCache(bool bAtClosing)
     925             : 
     926             : {
     927         315 :     return BlockBasedFlushCache(bAtClosing);
     928             : }
     929             : 
     930             : /************************************************************************/
     931             : /*                           TrySaveXML()                               */
     932             : /************************************************************************/
     933             : 
     934         135 : CPLErr TileDBRasterDataset::TrySaveXML()
     935             : 
     936             : {
     937         135 :     if (m_array == nullptr)
     938           0 :         return CE_None;
     939             : 
     940         135 :     CPLXMLNode *psTree = nullptr;
     941             :     try
     942             :     {
     943         135 :         nPamFlags &= ~GPF_DIRTY;
     944             : 
     945         135 :         if (psPam == nullptr || (nPamFlags & GPF_NOSAVE))
     946           0 :             return CE_None;
     947             : 
     948             :         /* --------------------------------------------------------------------
     949             :          */
     950             :         /*      Make sure we know the filename we want to store in. */
     951             :         /* --------------------------------------------------------------------
     952             :          */
     953         135 :         if (!BuildPamFilename())
     954           0 :             return CE_None;
     955             : 
     956             :         /* --------------------------------------------------------------------
     957             :          */
     958             :         /*      Build the XML representation of the auxiliary metadata. */
     959             :         /* --------------------------------------------------------------------
     960             :          */
     961         135 :         psTree = SerializeToXML(nullptr);
     962             : 
     963         135 :         if (psTree == nullptr)
     964             :         {
     965             :             /* If we have unset all metadata, we have to delete the PAM file */
     966           0 :             m_array->delete_metadata(GDAL_ATTRIBUTE_NAME);
     967             : 
     968           0 :             if (m_bDatasetInGroup)
     969             :             {
     970           0 :                 tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
     971           0 :                 group.delete_metadata(GDAL_ATTRIBUTE_NAME);
     972             :             }
     973             : 
     974           0 :             return CE_None;
     975             :         }
     976             : 
     977         135 :         if (m_poSubDatasetsTree)
     978             :         {
     979           3 :             CPLAddXMLChild(psTree,
     980           3 :                            CPLCloneXMLTree(m_poSubDatasetsTree->psChild));
     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         135 :         if (!psPam->osSubdatasetName.empty())
     991             :         {
     992             :             CPLXMLNode *psOldTree, *psSubTree;
     993             : 
     994           0 :             CPLErrorReset();
     995             :             {
     996           0 :                 CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
     997           0 :                 psOldTree = CPLParseXMLFile(psPam->pszPamFilename);
     998             :             }
     999             : 
    1000           0 :             if (psOldTree == nullptr)
    1001             :                 psOldTree =
    1002           0 :                     CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset");
    1003             : 
    1004           0 :             for (psSubTree = psOldTree->psChild; psSubTree != nullptr;
    1005           0 :                  psSubTree = psSubTree->psNext)
    1006             :             {
    1007           0 :                 if (psSubTree->eType != CXT_Element ||
    1008           0 :                     !EQUAL(psSubTree->pszValue, "Subdataset"))
    1009           0 :                     continue;
    1010             : 
    1011           0 :                 if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
    1012             :                            psPam->osSubdatasetName))
    1013           0 :                     continue;
    1014             : 
    1015           0 :                 break;
    1016             :             }
    1017             : 
    1018           0 :             if (psSubTree == nullptr)
    1019             :             {
    1020             :                 psSubTree =
    1021           0 :                     CPLCreateXMLNode(psOldTree, CXT_Element, "Subdataset");
    1022           0 :                 CPLCreateXMLNode(
    1023             :                     CPLCreateXMLNode(psSubTree, CXT_Attribute, "name"),
    1024           0 :                     CXT_Text, psPam->osSubdatasetName);
    1025             :             }
    1026             : 
    1027             :             CPLXMLNode *psOldPamDataset =
    1028           0 :                 CPLGetXMLNode(psSubTree, "PAMDataset");
    1029           0 :             if (psOldPamDataset != nullptr)
    1030             :             {
    1031           0 :                 CPLRemoveXMLChild(psSubTree, psOldPamDataset);
    1032           0 :                 CPLDestroyXMLNode(psOldPamDataset);
    1033             :             }
    1034             : 
    1035           0 :             CPLAddXMLChild(psSubTree, psTree);
    1036           0 :             psTree = psOldTree;
    1037             :         }
    1038             : 
    1039         135 :         if (m_nOverviewCountFromMetadata)
    1040             :         {
    1041           4 :             CPLCreateXMLElementAndValue(
    1042             :                 psTree, OVERVIEW_COUNT_KEY,
    1043             :                 CPLSPrintf("%d", m_nOverviewCountFromMetadata));
    1044             :         }
    1045             : 
    1046             :         /* --------------------------------------------------------------------
    1047             :          */
    1048             :         /*      Try saving the auxiliary metadata. */
    1049             :         /* --------------------------------------------------------------------
    1050             :          */
    1051         270 :         CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
    1052         135 :         char *pszTree = CPLSerializeXMLTree(psTree);
    1053             : 
    1054           0 :         std::unique_ptr<tiledb::Array> poArrayToClose;
    1055         135 :         tiledb::Array *poArray = m_array.get();
    1056         135 :         if (eAccess == GA_ReadOnly)
    1057             :         {
    1058           1 :             if (nTimestamp)
    1059             :             {
    1060           0 :                 poArrayToClose = std::make_unique<tiledb::Array>(
    1061           0 :                     *m_ctx, m_array->uri(), TILEDB_WRITE,
    1062           0 :                     tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
    1063             :             }
    1064             :             else
    1065             :             {
    1066           2 :                 poArrayToClose = std::make_unique<tiledb::Array>(
    1067           3 :                     *m_ctx, m_array->uri(), TILEDB_WRITE);
    1068             :             }
    1069           1 :             poArray = poArrayToClose.get();
    1070             :         }
    1071             : 
    1072         135 :         poArray->put_metadata(DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    1073             :                               static_cast<int>(strlen(RASTER_DATASET_TYPE)),
    1074             :                               RASTER_DATASET_TYPE);
    1075         135 :         poArray->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
    1076         135 :                               static_cast<int>(strlen(pszTree)), pszTree);
    1077             : 
    1078         135 :         if (poArrayToClose)
    1079           1 :             poArrayToClose->close();
    1080             : 
    1081         135 :         if (m_bDatasetInGroup)
    1082             :         {
    1083         369 :             tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    1084         123 :             group.put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    1085         123 :                                static_cast<int>(strlen(pszTree)), pszTree);
    1086         123 :             group.close();
    1087             :         }
    1088             : 
    1089         135 :         CPLFree(pszTree);
    1090             : 
    1091             :         /* --------------------------------------------------------------------
    1092             :          */
    1093             :         /*      Cleanup */
    1094             :         /* --------------------------------------------------------------------
    1095             :          */
    1096         135 :         if (psTree)
    1097         135 :             CPLDestroyXMLNode(psTree);
    1098             : 
    1099         135 :         return CE_None;
    1100             :     }
    1101           0 :     catch (const std::exception &e)
    1102             :     {
    1103           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1104           0 :         if (psTree)
    1105           0 :             CPLDestroyXMLNode(psTree);
    1106           0 :         return CE_Failure;
    1107             :     }
    1108             : }
    1109             : 
    1110             : /************************************************************************/
    1111             : /*                           TryLoadXML()                               */
    1112             : /************************************************************************/
    1113             : 
    1114         129 : CPLErr TileDBRasterDataset::TryLoadXML(CSLConstList papszSiblingFiles)
    1115             : 
    1116             : {
    1117         129 :     return TryLoadCachedXML(papszSiblingFiles, true);
    1118             : }
    1119             : 
    1120             : /************************************************************************/
    1121             : /*                           TryLoadCachedXML()                               */
    1122             : /************************************************************************/
    1123             : 
    1124         257 : CPLErr TileDBRasterDataset::TryLoadCachedXML(CSLConstList /*papszSiblingFiles*/,
    1125             :                                              bool bReload)
    1126             : 
    1127             : {
    1128         257 :     CPLXMLNode *psTree = nullptr;
    1129             :     try
    1130             :     {
    1131         257 :         PamInitialize();
    1132         514 :         tiledb::VFS vfs(*m_ctx, m_ctx->config());
    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         257 :         nPamFlags &= ~GPF_DIRTY;
    1145             : 
    1146             :         /* --------------------------------------------------------------------
    1147             :          */
    1148             :         /*      Try reading the file. */
    1149             :         /* --------------------------------------------------------------------
    1150             :          */
    1151         257 :         if (!BuildPamFilename())
    1152           0 :             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         257 :         CPLErr eLastErr = CPLGetLastErrorType();
    1162         257 :         int nLastErrNo = CPLGetLastErrorNo();
    1163         514 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
    1164             : 
    1165         257 :         CPLErrorReset();
    1166             :         {
    1167         514 :             CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
    1168             : 
    1169         257 :             if (bReload)
    1170             :             {
    1171         129 :                 tiledb_datatype_t v_type =
    1172             :                     TILEDB_UINT8;  // CPLSerializeXMLTree returns char*
    1173         129 :                 const void *v_r = nullptr;
    1174         129 :                 uint32_t v_num = 0;
    1175          23 :                 auto &oArray = ((eAccess == GA_Update) && (m_roArray))
    1176             :                                    ? m_roArray
    1177         152 :                                    : m_array;
    1178         129 :                 oArray->get_metadata(GDAL_ATTRIBUTE_NAME, &v_type, &v_num,
    1179             :                                      &v_r);
    1180         128 :                 if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
    1181           0 :                             v_type == TILEDB_STRING_ASCII ||
    1182         129 :                             v_type == TILEDB_STRING_UTF8))
    1183             :                 {
    1184             :                     osMetaDoc =
    1185         128 :                         CPLString(static_cast<const char *>(v_r), v_num);
    1186             :                 }
    1187         129 :                 psTree = CPLParseXMLString(osMetaDoc);
    1188             :             }
    1189             : 
    1190         258 :             if (bReload && psTree == nullptr &&
    1191         258 :                 vfs.is_file(psPam->pszPamFilename))
    1192             :             {
    1193           1 :                 auto nBytes = vfs.file_size(psPam->pszPamFilename);
    1194           2 :                 tiledb::VFS::filebuf fbuf(vfs);
    1195           1 :                 fbuf.open(psPam->pszPamFilename, std::ios::in);
    1196           1 :                 std::istream is(&fbuf);
    1197           1 :                 osMetaDoc.resize(nBytes);
    1198           1 :                 is.read(&osMetaDoc[0], nBytes);
    1199           1 :                 fbuf.close();
    1200           1 :                 psTree = CPLParseXMLString(osMetaDoc);
    1201             :             }
    1202             : 
    1203         257 :             if (!bReload)
    1204             :             {
    1205         128 :                 psTree = CPLParseXMLString(osMetaDoc);
    1206             :             }
    1207             :         }
    1208         257 :         CPLErrorReset();
    1209             : 
    1210         257 :         if (eLastErr != CE_None)
    1211           0 :             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         257 :         if (psTree && !psPam->osSubdatasetName.empty())
    1220             :         {
    1221          11 :             CPLXMLNode *psSubTree = psTree->psChild;
    1222             : 
    1223          65 :             for (; psSubTree != nullptr; psSubTree = psSubTree->psNext)
    1224             :             {
    1225          64 :                 if (psSubTree->eType != CXT_Element ||
    1226          64 :                     !EQUAL(psSubTree->pszValue, "Subdataset"))
    1227          38 :                     continue;
    1228             : 
    1229          26 :                 if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
    1230             :                            psPam->osSubdatasetName))
    1231          16 :                     continue;
    1232             : 
    1233          10 :                 psSubTree = CPLGetXMLNode(psSubTree, "PAMDataset");
    1234          10 :                 break;
    1235             :             }
    1236             : 
    1237          11 :             if (psSubTree != nullptr)
    1238          10 :                 psSubTree = CPLCloneXMLTree(psSubTree);
    1239             : 
    1240          11 :             CPLDestroyXMLNode(psTree);
    1241          11 :             psTree = psSubTree;
    1242             :         }
    1243         257 :         if (psTree == nullptr)
    1244           1 :             return CE_Failure;
    1245             : 
    1246         256 :         m_nOverviewCountFromMetadata =
    1247         256 :             atoi(CPLGetXMLValue(psTree, OVERVIEW_COUNT_KEY, "0"));
    1248         256 :         if (m_nOverviewCountFromMetadata)
    1249             :         {
    1250          10 :             CPLDebugOnly("TileDB", "OverviewCount = %d",
    1251             :                          m_nOverviewCountFromMetadata);
    1252             :         }
    1253             : 
    1254             :         /* --------------------------------------------------------------------
    1255             :          */
    1256             :         /*      Initialize ourselves from this XML tree. */
    1257             :         /* --------------------------------------------------------------------
    1258             :          */
    1259             : 
    1260         256 :         CPLString osPath(CPLGetPathSafe(psPam->pszPamFilename));
    1261         256 :         const CPLErr eErr = XMLInit(psTree, osPath);
    1262             : 
    1263         256 :         CPLDestroyXMLNode(psTree);
    1264             : 
    1265         256 :         if (eErr != CE_None)
    1266           0 :             PamClear();
    1267             : 
    1268         256 :         return eErr;
    1269             :     }
    1270           0 :     catch (const tiledb::TileDBError &e)
    1271             :     {
    1272           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1273           0 :         if (psTree)
    1274           0 :             CPLDestroyXMLNode(psTree);
    1275           0 :         return CE_Failure;
    1276             :     }
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                             GetMetadata()                            */
    1281             : /************************************************************************/
    1282             : 
    1283         216 : char **TileDBRasterDataset::GetMetadata(const char *pszDomain)
    1284             : 
    1285             : {
    1286         216 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
    1287             :     {
    1288           6 :         if (m_aosSubdatasetMD.empty())
    1289             :         {
    1290           5 :             CSLConstList papszMD = GDALPamDataset::GetMetadata(pszDomain);
    1291           8 :             for (const auto &[pszKey, pszValue] :
    1292          13 :                  cpl::IterateNameValue(papszMD))
    1293             :             {
    1294           4 :                 if (STARTS_WITH(pszKey, "SUBDATASET_") &&
    1295           4 :                     EQUAL(pszKey + strlen(pszKey) - strlen("_NAME"), "_NAME") &&
    1296           2 :                     !STARTS_WITH(pszValue, "TILEDB:"))
    1297             :                 {
    1298           2 :                     m_aosSubdatasetMD.AddNameValue(
    1299           2 :                         pszKey, CPLString().Printf("TILEDB:\"%s\":%s",
    1300           2 :                                                    GetDescription(), pszValue));
    1301             :                 }
    1302             :                 else
    1303             :                 {
    1304           2 :                     m_aosSubdatasetMD.AddNameValue(pszKey, pszValue);
    1305             :                 }
    1306             :             }
    1307             :         }
    1308           6 :         return m_aosSubdatasetMD.List();
    1309             :     }
    1310             :     else
    1311             :     {
    1312         210 :         return GDALPamDataset::GetMetadata(pszDomain);
    1313             :     }
    1314             : }
    1315             : 
    1316             : /************************************************************************/
    1317             : /*                                Open()                                */
    1318             : /************************************************************************/
    1319             : 
    1320         129 : GDALDataset *TileDBRasterDataset::Open(GDALOpenInfo *poOpenInfo,
    1321             :                                        tiledb::Object::Type objectType)
    1322             : 
    1323             : {
    1324             :     try
    1325             :     {
    1326         129 :         return OpenInternal(poOpenInfo, objectType);
    1327             :     }
    1328           0 :     catch (const tiledb::TileDBError &e)
    1329             :     {
    1330           0 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: Open(%s) failed: %s",
    1331           0 :                  poOpenInfo->pszFilename, e.what());
    1332           0 :         return nullptr;
    1333             :     }
    1334             : }
    1335             : 
    1336             : /************************************************************************/
    1337             : /*                             OpenInternal()                           */
    1338             : /************************************************************************/
    1339             : 
    1340         129 : GDALDataset *TileDBRasterDataset::OpenInternal(GDALOpenInfo *poOpenInfo,
    1341             :                                                tiledb::Object::Type objectType)
    1342             : 
    1343             : {
    1344         258 :     auto poDS = std::make_unique<TileDBRasterDataset>();
    1345             : 
    1346         129 :     poDS->m_bDeferredCreateHasRun = true;
    1347         129 :     poDS->m_bDeferredCreateHasBeenSuccessful = true;
    1348             : 
    1349             :     const char *pszConfig =
    1350         129 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
    1351             : 
    1352             :     const char *pszTimestamp =
    1353         129 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_TIMESTAMP");
    1354             : 
    1355         258 :     poDS->bStats =
    1356         129 :         CSLFetchBoolean(poOpenInfo->papszOpenOptions, "STATS", FALSE);
    1357             : 
    1358         129 :     if (pszConfig != nullptr)
    1359             :     {
    1360           0 :         poDS->m_osConfigFilename = pszConfig;
    1361           0 :         tiledb::Config cfg(pszConfig);
    1362           0 :         poDS->m_ctx.reset(new tiledb::Context(cfg));
    1363             :     }
    1364             :     else
    1365             :     {
    1366         129 :         poDS->m_ctx.reset(new tiledb::Context());
    1367             :     }
    1368         129 :     if (pszTimestamp)
    1369          18 :         poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
    1370             : 
    1371         258 :     CPLString osURI;
    1372         258 :     CPLString osSubdataset;
    1373             : 
    1374         258 :     std::string osAttrNameTmp;
    1375             :     const char *pszAttr =
    1376         129 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_ATTRIBUTE");
    1377             : 
    1378         129 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
    1379           2 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
    1380             :     {
    1381             :         // form required read attributes and open file
    1382             :         // Create a corresponding GDALDataset.
    1383             :         CPLStringList apszName(
    1384           2 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
    1385           2 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
    1386             : 
    1387           2 :         if (apszName.size() != 3)
    1388             :         {
    1389           0 :             return nullptr;
    1390             :         }
    1391             : 
    1392           2 :         osURI = TileDBDataset::VSI_to_tiledb_uri(apszName[1]);
    1393           2 :         osSubdataset = apszName[2];
    1394           4 :         poDS->SetSubdatasetName(osSubdataset.c_str());
    1395             :     }
    1396             :     else
    1397             :     {
    1398         127 :         if (pszAttr != nullptr)
    1399             :         {
    1400           4 :             poDS->SetSubdatasetName(pszAttr);
    1401             :         }
    1402             : 
    1403         127 :         osURI = TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
    1404             :     }
    1405             : 
    1406         258 :     CPLString osAux = CPLGetBasenameSafe(osURI);
    1407         129 :     osAux += ".tdb";
    1408             : 
    1409             :     // aux file is in array folder
    1410         258 :     poDS->SetPhysicalFilename(
    1411         258 :         CPLFormFilenameSafe(osURI, osAux, nullptr).c_str());
    1412             :     // Initialize any PAM information.
    1413         129 :     poDS->SetDescription(osURI);
    1414             : 
    1415         129 :     poDS->m_osArrayURI = osURI;
    1416         129 :     if (objectType == tiledb::Object::Type::Group)
    1417             :     {
    1418         116 :         poDS->m_bDatasetInGroup = true;
    1419             : 
    1420             :         // Full resolution raster array
    1421         116 :         poDS->m_osArrayURI = CPLFormFilenameSafe(osURI.c_str(), "l_0", nullptr);
    1422             :     }
    1423             : 
    1424         235 :     if ((poOpenInfo->eAccess == GA_ReadOnly) &&
    1425         106 :         (!TileDBDataset::TileDBObjectExists(poDS->m_osArrayURI)))
    1426             :     {
    1427           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1428             :                  "Failed to open %s as an array or group.",
    1429             :                  poOpenInfo->pszFilename);
    1430           0 :         return nullptr;
    1431             :     }
    1432             : 
    1433         129 :     const tiledb_query_type_t eMode =
    1434         129 :         poOpenInfo->eAccess == GA_Update ? TILEDB_WRITE : TILEDB_READ;
    1435             : 
    1436         129 :     if (poDS->nTimestamp)
    1437             :     {
    1438          36 :         poDS->m_array = std::make_unique<tiledb::Array>(
    1439          18 :             *poDS->m_ctx, poDS->m_osArrayURI, eMode,
    1440          54 :             tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp));
    1441             :     }
    1442             :     else
    1443             :     {
    1444         222 :         poDS->m_array = std::make_unique<tiledb::Array>(
    1445         222 :             *poDS->m_ctx, poDS->m_osArrayURI, eMode);
    1446             :     }
    1447             : 
    1448         129 :     poDS->eAccess = poOpenInfo->eAccess;
    1449             : 
    1450             :     // Force opening read-only dataset
    1451         129 :     if (eMode == TILEDB_WRITE)
    1452             :     {
    1453          23 :         tiledb::Context *ctx = nullptr;
    1454          23 :         poDS->GetArray(false, ctx);
    1455             :     }
    1456             : 
    1457             :     // dependent on PAM metadata for information about array
    1458         129 :     poDS->TryLoadXML();
    1459             : 
    1460         258 :     tiledb::ArraySchema schema = poDS->m_array->schema();
    1461             : 
    1462         129 :     char **papszStructMeta = poDS->GetMetadata("IMAGE_STRUCTURE");
    1463         129 :     const char *pszXSize = CSLFetchNameValue(papszStructMeta, "X_SIZE");
    1464         129 :     if (pszXSize)
    1465             :     {
    1466         113 :         poDS->nRasterXSize = atoi(pszXSize);
    1467         113 :         if (poDS->nRasterXSize <= 0)
    1468             :         {
    1469           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1470             :                      "Width %i should be greater than zero.",
    1471           0 :                      poDS->nRasterXSize);
    1472           0 :             return nullptr;
    1473             :         }
    1474             :     }
    1475             : 
    1476         129 :     const char *pszYSize = CSLFetchNameValue(papszStructMeta, "Y_SIZE");
    1477         129 :     if (pszYSize)
    1478             :     {
    1479         113 :         poDS->nRasterYSize = atoi(pszYSize);
    1480         113 :         if (poDS->nRasterYSize <= 0)
    1481             :         {
    1482           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1483             :                      "Height %i should be greater than zero.",
    1484           0 :                      poDS->nRasterYSize);
    1485           0 :             return nullptr;
    1486             :         }
    1487             :     }
    1488             : 
    1489         129 :     const char *pszNBits = CSLFetchNameValue(papszStructMeta, "NBITS");
    1490         129 :     if (pszNBits)
    1491             :     {
    1492         113 :         poDS->nBitsPerSample = atoi(pszNBits);
    1493             :     }
    1494             : 
    1495         129 :     const char *pszDataType = CSLFetchNameValue(papszStructMeta, "DATA_TYPE");
    1496         129 :     if (pszDataType)
    1497             :     {
    1498             :         // handle the case where arrays have been written with int type
    1499             :         // (2.5.0)
    1500         113 :         GDALDataType eDT = GDALGetDataTypeByName(pszDataType);
    1501         113 :         if (eDT == GDT_Unknown)
    1502             :         {
    1503           1 :             int t = atoi(pszDataType);
    1504           1 :             if ((t > 0) && (t < GDT_TypeCount))
    1505           1 :                 poDS->eDataType = static_cast<GDALDataType>(atoi(pszDataType));
    1506             :             else
    1507             :             {
    1508           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Unknown data type %s.",
    1509             :                          pszDataType);
    1510           0 :                 return nullptr;
    1511             :             }
    1512             :         }
    1513             :         else
    1514             :         {
    1515         112 :             poDS->eDataType = eDT;
    1516             :         }
    1517             :     }
    1518             :     else
    1519             :     {
    1520          16 :         if (!pszAttr)
    1521             :         {
    1522          16 :             if (schema.attribute_num() == 1)
    1523             :             {
    1524          14 :                 osAttrNameTmp = schema.attribute(0).name();
    1525          14 :                 pszAttr = osAttrNameTmp.c_str();
    1526             :             }
    1527             :         }
    1528             :     }
    1529             : 
    1530         129 :     const char *pszIndexMode = CSLFetchNameValue(papszStructMeta, "INTERLEAVE");
    1531             : 
    1532         129 :     if (pszIndexMode)
    1533         107 :         option_to_index_type(pszIndexMode, poDS->eIndexMode);
    1534             : 
    1535         258 :     std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
    1536             : 
    1537         129 :     int iYDim = 0;
    1538         129 :     int iXDim = 1;
    1539         129 :     if ((dims.size() == 2) || (dims.size() == 3))
    1540             :     {
    1541         129 :         if (dims.size() == 3)
    1542             :         {
    1543         141 :             if ((pszAttr != nullptr) &&
    1544         141 :                 (schema.attributes().count(pszAttr) == 0))
    1545             :             {
    1546           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1547             :                          "%s attribute is not found in TileDB schema.",
    1548             :                          pszAttr);
    1549           0 :                 return nullptr;
    1550             :             }
    1551             : 
    1552         123 :             if (poDS->eIndexMode == PIXEL)
    1553          15 :                 std::rotate(dims.begin(), dims.begin() + 2, dims.end());
    1554             : 
    1555         123 :             if (dims[0].type() != TILEDB_UINT64)
    1556             :             {
    1557           0 :                 const char *pszTypeName = "";
    1558           0 :                 tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1559           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1560             :                          "Unsupported BAND dimension type: %s", pszTypeName);
    1561           0 :                 return nullptr;
    1562             :             }
    1563         123 :             poDS->nBandStart = dims[0].domain<uint64_t>().first;
    1564         123 :             const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
    1565         246 :             if (nBandEnd < poDS->nBandStart ||
    1566         123 :                 nBandEnd - poDS->nBandStart >
    1567         123 :                     static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1568             :             {
    1569           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1570             :                          "Invalid bounds for BAND dimension.");
    1571           0 :                 return nullptr;
    1572             :             }
    1573         123 :             poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
    1574         123 :             iYDim = 1;
    1575         123 :             iXDim = 2;
    1576             :         }
    1577             :         else
    1578             :         {
    1579             :             const char *pszBands =
    1580           6 :                 poDS->GetMetadataItem("NUM_BANDS", "IMAGE_STRUCTURE");
    1581           6 :             if (pszBands)
    1582             :             {
    1583           1 :                 poDS->nBands = atoi(pszBands);
    1584             :             }
    1585             : 
    1586           6 :             poDS->eIndexMode = ATTRIBUTES;
    1587             :         }
    1588             :     }
    1589             :     else
    1590             :     {
    1591           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1592             :                  "Wrong number of dimensions %d: expected 2 or 3.",
    1593           0 :                  static_cast<int>(dims.size()));
    1594           0 :         return nullptr;
    1595             :     }
    1596             : 
    1597         258 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
    1598         129 :         !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
    1599             :     {
    1600           0 :         return nullptr;
    1601             :     }
    1602             : 
    1603         129 :     if (dims[iYDim].type() != TILEDB_UINT64)
    1604             :     {
    1605           0 :         const char *pszTypeName = "";
    1606           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1607           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1608             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1609           0 :         return nullptr;
    1610             :     }
    1611         129 :     if (!pszYSize)
    1612             :     {
    1613          16 :         const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
    1614          16 :         const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
    1615          32 :         if (nStart != 0 ||
    1616          16 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1617             :         {
    1618           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1619             :                      "Invalid bounds for Y dimension.");
    1620           0 :             return nullptr;
    1621             :         }
    1622          16 :         poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
    1623             :     }
    1624         129 :     const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
    1625         129 :     if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1626             :     {
    1627           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
    1628           0 :         return nullptr;
    1629             :     }
    1630         129 :     poDS->nBlockYSize = static_cast<int>(nBlockYSize);
    1631             : 
    1632         129 :     if (dims[iXDim].type() != TILEDB_UINT64)
    1633             :     {
    1634           0 :         const char *pszTypeName = "";
    1635           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1636           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1637             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1638           0 :         return nullptr;
    1639             :     }
    1640         129 :     if (!pszXSize)
    1641             :     {
    1642          16 :         const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
    1643          16 :         const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
    1644          32 :         if (nStart != 0 ||
    1645          16 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1646             :         {
    1647           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1648             :                      "Invalid bounds for X dimension.");
    1649           0 :             return nullptr;
    1650             :         }
    1651          16 :         poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
    1652             :     }
    1653         129 :     const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
    1654         129 :     if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1655             :     {
    1656           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
    1657           0 :         return nullptr;
    1658             :     }
    1659         129 :     poDS->nBlockXSize = static_cast<int>(nBlockXSize);
    1660             : 
    1661         129 :     poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
    1662         129 :     poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
    1663             : 
    1664         129 :     if (dims.size() == 3)
    1665             :     {
    1666             :         // Create band information objects.
    1667         844 :         for (int i = 1; i <= poDS->nBands; ++i)
    1668             :         {
    1669         721 :             if (pszAttr == nullptr)
    1670         189 :                 poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
    1671             :             else
    1672        1064 :                 poDS->SetBand(
    1673        1064 :                     i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
    1674             :         }
    1675             :     }
    1676             :     else  // subdatasets or only attributes
    1677             :     {
    1678           7 :         if ((poOpenInfo->eAccess == GA_Update) &&
    1679           1 :             (poDS->GetMetadata("SUBDATASETS") != nullptr))
    1680             :         {
    1681           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1682             :                      "The TileDB driver does not support update access "
    1683             :                      "to subdatasets.");
    1684           0 :             return nullptr;
    1685             :         }
    1686             : 
    1687           6 :         if (!osSubdataset.empty())
    1688             :         {
    1689             :             // do we have the attribute in the schema
    1690           2 :             if (schema.attributes().count(osSubdataset))
    1691             :             {
    1692           0 :                 poDS->SetBand(
    1693           0 :                     1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
    1694             :             }
    1695             :             else
    1696             :             {
    1697           2 :                 if (schema.attributes().count(osSubdataset + "_1"))
    1698             :                 {
    1699             :                     // Create band information objects.
    1700           2 :                     for (int i = 1; i <= poDS->nBands; ++i)
    1701             :                     {
    1702           1 :                         CPLString osAttr = CPLString().Printf(
    1703           2 :                             "%s_%d", osSubdataset.c_str(), i);
    1704           2 :                         poDS->SetBand(
    1705           1 :                             i, new TileDBRasterBand(poDS.get(), i, osAttr));
    1706             :                     }
    1707             :                 }
    1708             :                 else
    1709             :                 {
    1710           1 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1711             :                              "%s attribute is not found in TileDB schema.",
    1712             :                              osSubdataset.c_str());
    1713           1 :                     return nullptr;
    1714             :                 }
    1715             :             }
    1716             :         }
    1717             :         else
    1718             :         {
    1719           4 :             char **papszMeta = poDS->GetMetadata("SUBDATASETS");
    1720           4 :             if (papszMeta != nullptr)
    1721             :             {
    1722           1 :                 if ((CSLCount(papszMeta) / 2) == 1)
    1723             :                 {
    1724             :                     const char *pszSubDSName =
    1725           0 :                         poDS->m_aosSubdatasetMD.FetchNameValueDef(
    1726             :                             "SUBDATASET_1_NAME", "");
    1727           0 :                     return GDALDataset::FromHandle(
    1728           0 :                         GDALOpen(pszSubDSName, poOpenInfo->eAccess));
    1729             :                 }
    1730             :             }
    1731           3 :             else if (poDS->eIndexMode == ATTRIBUTES)
    1732             :             {
    1733           3 :                 poDS->nBands = schema.attribute_num();
    1734             : 
    1735             :                 // Create band information objects.
    1736          11 :                 for (int i = 1; i <= poDS->nBands; ++i)
    1737             :                 {
    1738             :                     CPLString osAttr =
    1739          24 :                         TILEDB_VALUES + CPLString().Printf("_%i", i);
    1740          16 :                     poDS->SetBand(i,
    1741           8 :                                   new TileDBRasterBand(poDS.get(), i, osAttr));
    1742             :                 }
    1743             :             }
    1744             :             else
    1745             :             {
    1746           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1747             :                          "%s is missing required TileDB subdataset metadata.",
    1748             :                          osURI.c_str());
    1749           0 :                 return nullptr;
    1750             :             }
    1751             :         }
    1752             :     }
    1753             : 
    1754             :     // reload metadata now that bands are created to populate band metadata
    1755         128 :     poDS->TryLoadCachedXML(nullptr, false);
    1756             : 
    1757         256 :     tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
    1758             : 
    1759         128 :     if (!STARTS_WITH_CI(osURI, "TILEDB:") && vfs.is_dir(osURI))
    1760         128 :         poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
    1761             :     else
    1762           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1763             :                  "Overviews not supported for network writes.");
    1764             : 
    1765         128 :     return poDS.release();
    1766             : }
    1767             : 
    1768             : /************************************************************************/
    1769             : /*                              CreateAttribute()                       */
    1770             : /************************************************************************/
    1771             : 
    1772             : template <class T, class NoDataT = T>
    1773         129 : static tiledb::Attribute CreateAttribute(tiledb::Context &ctx,
    1774             :                                          const std::string &osAttrName,
    1775             :                                          tiledb::FilterList &filterList,
    1776             :                                          bool bHasFillValue, double dfFillValue)
    1777             : {
    1778         129 :     auto attr = tiledb::Attribute::create<T>(ctx, osAttrName, filterList);
    1779         129 :     if (bHasFillValue && GDALIsValueInRange<NoDataT>(dfFillValue))
    1780             :     {
    1781          30 :         const auto nVal = static_cast<NoDataT>(dfFillValue);
    1782          30 :         if (dfFillValue == static_cast<double>(nVal))
    1783             :         {
    1784             :             if constexpr (sizeof(T) == sizeof(NoDataT))
    1785             :             {
    1786          26 :                 attr.set_fill_value(&nVal, sizeof(nVal));
    1787             :             }
    1788             :             else
    1789             :             {
    1790           4 :                 T aVal = {nVal, nVal};
    1791           4 :                 attr.set_fill_value(&aVal, sizeof(aVal));
    1792             :             }
    1793             :         }
    1794             :     }
    1795         129 :     return attr;
    1796             : }
    1797             : 
    1798             : /************************************************************************/
    1799             : /*                              CreateAttribute()                       */
    1800             : /************************************************************************/
    1801             : 
    1802         123 : CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
    1803             :                                             const CPLString &osAttrName,
    1804             :                                             const int nSubRasterCount,
    1805             :                                             bool bHasFillValue,
    1806             :                                             double dfFillValue)
    1807             : {
    1808             :     try
    1809             :     {
    1810         252 :         for (int i = 0; i < nSubRasterCount; ++i)
    1811             :         {
    1812         129 :             CPLString osName(osAttrName);
    1813             :             // a few special cases
    1814             :             // remove any leading slashes or
    1815             :             // additional slashes as in the case of hdf5
    1816         129 :             if STARTS_WITH (osName, "//")
    1817             :             {
    1818           2 :                 osName = osName.substr(2);
    1819             :             }
    1820             : 
    1821         129 :             osName.replaceAll("/", "_");
    1822         129 :             CPLString osPrettyName = osName;
    1823             : 
    1824         129 :             if ((eIndexMode == ATTRIBUTES) ||
    1825         115 :                 ((m_bHasSubDatasets) && (nSubRasterCount > 1)))
    1826             :             {
    1827          14 :                 osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
    1828             :             }
    1829             : 
    1830         129 :             switch (eType)
    1831             :             {
    1832          53 :                 case GDT_Byte:
    1833             :                 {
    1834         159 :                     m_schema->add_attribute(::CreateAttribute<unsigned char>(
    1835          53 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1836         106 :                         dfFillValue));
    1837          53 :                     nBitsPerSample = 8;
    1838          53 :                     break;
    1839             :                 }
    1840           4 :                 case GDT_Int8:
    1841             :                 {
    1842           4 :                     m_schema->add_attribute(
    1843           8 :                         ::CreateAttribute<int8_t>(*m_ctx, osName, *m_filterList,
    1844           8 :                                                   bHasFillValue, dfFillValue));
    1845           4 :                     nBitsPerSample = 8;
    1846           4 :                     break;
    1847             :                 }
    1848           5 :                 case GDT_UInt16:
    1849             :                 {
    1850          15 :                     m_schema->add_attribute(::CreateAttribute<uint16_t>(
    1851           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1852          10 :                         dfFillValue));
    1853           5 :                     nBitsPerSample = 16;
    1854           5 :                     break;
    1855             :                 }
    1856           5 :                 case GDT_UInt32:
    1857             :                 {
    1858          15 :                     m_schema->add_attribute(::CreateAttribute<uint32_t>(
    1859           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1860          10 :                         dfFillValue));
    1861           5 :                     nBitsPerSample = 32;
    1862           5 :                     break;
    1863             :                 }
    1864           4 :                 case GDT_UInt64:
    1865             :                 {
    1866          12 :                     m_schema->add_attribute(::CreateAttribute<uint64_t>(
    1867           4 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1868           8 :                         dfFillValue));
    1869           4 :                     nBitsPerSample = 64;
    1870           4 :                     break;
    1871             :                 }
    1872           5 :                 case GDT_Int16:
    1873             :                 {
    1874          15 :                     m_schema->add_attribute(::CreateAttribute<int16_t>(
    1875           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1876          10 :                         dfFillValue));
    1877           5 :                     nBitsPerSample = 16;
    1878           5 :                     break;
    1879             :                 }
    1880           7 :                 case GDT_Int32:
    1881             :                 {
    1882          21 :                     m_schema->add_attribute(::CreateAttribute<int32_t>(
    1883           7 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1884          14 :                         dfFillValue));
    1885           7 :                     nBitsPerSample = 32;
    1886           7 :                     break;
    1887             :                 }
    1888           4 :                 case GDT_Int64:
    1889             :                 {
    1890          12 :                     m_schema->add_attribute(::CreateAttribute<int64_t>(
    1891           4 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1892           8 :                         dfFillValue));
    1893           4 :                     nBitsPerSample = 64;
    1894           4 :                     break;
    1895             :                 }
    1896          12 :                 case GDT_Float32:
    1897             :                 {
    1898          12 :                     m_schema->add_attribute(
    1899          24 :                         ::CreateAttribute<float>(*m_ctx, osName, *m_filterList,
    1900          24 :                                                  bHasFillValue, dfFillValue));
    1901          12 :                     nBitsPerSample = 32;
    1902          12 :                     break;
    1903             :                 }
    1904           8 :                 case GDT_Float64:
    1905             :                 {
    1906           8 :                     m_schema->add_attribute(
    1907          16 :                         ::CreateAttribute<double>(*m_ctx, osName, *m_filterList,
    1908          16 :                                                   bHasFillValue, dfFillValue));
    1909           8 :                     nBitsPerSample = 64;
    1910           8 :                     break;
    1911             :                 }
    1912           5 :                 case GDT_CInt16:
    1913             :                 {
    1914           5 :                     m_schema->add_attribute(
    1915          10 :                         ::CreateAttribute<int16_t[2], int16_t>(
    1916           5 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1917          10 :                             dfFillValue));
    1918           5 :                     nBitsPerSample = 16;
    1919           5 :                     break;
    1920             :                 }
    1921           5 :                 case GDT_CInt32:
    1922             :                 {
    1923           5 :                     m_schema->add_attribute(
    1924          10 :                         ::CreateAttribute<int32_t[2], int32_t>(
    1925           5 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1926          10 :                             dfFillValue));
    1927           5 :                     nBitsPerSample = 32;
    1928           5 :                     break;
    1929             :                 }
    1930           5 :                 case GDT_CFloat32:
    1931             :                 {
    1932          15 :                     m_schema->add_attribute(::CreateAttribute<float[2], float>(
    1933           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1934          10 :                         dfFillValue));
    1935           5 :                     nBitsPerSample = 32;
    1936           5 :                     break;
    1937             :                 }
    1938           7 :                 case GDT_CFloat64:
    1939             :                 {
    1940           7 :                     m_schema->add_attribute(
    1941          14 :                         ::CreateAttribute<double[2], double>(
    1942           7 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1943          14 :                             dfFillValue));
    1944           7 :                     nBitsPerSample = 64;
    1945           7 :                     break;
    1946             :                 }
    1947           0 :                 default:
    1948           0 :                     return CE_Failure;
    1949             :             }
    1950             : 
    1951         129 :             if ((m_bHasSubDatasets) && (i == 0))
    1952             :             {
    1953           8 :                 CPLString osDim;
    1954           8 :                 switch (nSubRasterCount)
    1955             :                 {
    1956           0 :                     case 2:
    1957           0 :                         osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
    1958           0 :                         break;
    1959           8 :                     default:
    1960             :                         osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
    1961           8 :                                      nRasterYSize);
    1962           8 :                         break;
    1963             :                 }
    1964             : 
    1965           8 :                 const int nSubDataCount = 1 + m_aosSubdatasetMD.size() / 2;
    1966             :                 m_aosSubdatasetMD.SetNameValue(
    1967          16 :                     CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
    1968          24 :                     CPLString().Printf("%s", osPrettyName.c_str()));
    1969             : 
    1970             :                 m_aosSubdatasetMD.SetNameValue(
    1971          16 :                     CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
    1972           8 :                     CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
    1973             :                                        osPrettyName.c_str(),
    1974          16 :                                        GDALGetDataTypeName(eType)));
    1975             : 
    1976             :                 // add to PAM metadata
    1977           8 :                 if (!m_poSubDatasetsTree)
    1978             :                 {
    1979           3 :                     m_poSubDatasetsTree.reset(
    1980             :                         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
    1981             :                 }
    1982             : 
    1983           8 :                 CPLXMLNode *psSubNode = CPLCreateXMLNode(
    1984             :                     m_poSubDatasetsTree.get(), CXT_Element, "Subdataset");
    1985           8 :                 CPLAddXMLAttributeAndValue(psSubNode, "name",
    1986             :                                            osPrettyName.c_str());
    1987             : 
    1988           8 :                 CPLXMLNode *psMetaNode = CPLCreateXMLNode(
    1989             :                     CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
    1990             :                     CXT_Element, "Metadata");
    1991           8 :                 CPLAddXMLAttributeAndValue(psMetaNode, "domain",
    1992             :                                            "IMAGE_STRUCTURE");
    1993             : 
    1994           8 :                 CPLAddXMLAttributeAndValue(
    1995             :                     CPLCreateXMLElementAndValue(
    1996             :                         psMetaNode, "MDI",
    1997          16 :                         CPLString().Printf("%d", nRasterXSize)),
    1998             :                     "KEY", "X_SIZE");
    1999             : 
    2000           8 :                 CPLAddXMLAttributeAndValue(
    2001             :                     CPLCreateXMLElementAndValue(
    2002             :                         psMetaNode, "MDI",
    2003          16 :                         CPLString().Printf("%d", nRasterYSize)),
    2004             :                     "KEY", "Y_SIZE");
    2005             : 
    2006           8 :                 CPLAddXMLAttributeAndValue(
    2007             :                     CPLCreateXMLElementAndValue(
    2008             :                         psMetaNode, "MDI",
    2009          16 :                         CPLString().Printf("%s", GDALGetDataTypeName(eType))),
    2010             :                     "KEY", "DATA_TYPE");
    2011             : 
    2012           8 :                 if (m_lpoAttributeDS.size() > 0)
    2013             :                 {
    2014           6 :                     CPLAddXMLAttributeAndValue(
    2015             :                         CPLCreateXMLElementAndValue(
    2016             :                             psMetaNode, "MDI",
    2017          12 :                             CPLString().Printf("%d", nBands)),
    2018             :                         "KEY", "NUM_BANDS");
    2019             :                 }
    2020             :                 else
    2021             :                 {
    2022           2 :                     CPLAddXMLAttributeAndValue(
    2023             :                         CPLCreateXMLElementAndValue(
    2024             :                             psMetaNode, "MDI",
    2025           4 :                             CPLString().Printf("%d", nSubRasterCount)),
    2026             :                         "KEY", "NUM_BANDS");
    2027             :                 }
    2028             : 
    2029           8 :                 CPLAddXMLAttributeAndValue(
    2030             :                     CPLCreateXMLElementAndValue(
    2031             :                         psMetaNode, "MDI",
    2032          16 :                         CPLString().Printf("%d", nBitsPerSample)),
    2033             :                     "KEY", "NBITS");
    2034             :             }
    2035             :         }
    2036         123 :         return CE_None;
    2037             :     }
    2038           0 :     catch (const tiledb::TileDBError &e)
    2039             :     {
    2040           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2041           0 :         return CE_Failure;
    2042             :     }
    2043             : }
    2044             : 
    2045             : /************************************************************************/
    2046             : /*                              SetBlockSize()                          */
    2047             : /************************************************************************/
    2048             : 
    2049           1 : void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
    2050             :                                        CPLStringList &aosOptions)
    2051             : 
    2052             : {
    2053           1 :     int nX = 0;
    2054           1 :     int nY = 0;
    2055           1 :     poBand->GetBlockSize(&nX, &nY);
    2056             : 
    2057           1 :     if (!aosOptions.FetchNameValue("BLOCKXSIZE"))
    2058             :     {
    2059           1 :         aosOptions.SetNameValue("BLOCKXSIZE", CPLString().Printf("%d", nX));
    2060             :     }
    2061             : 
    2062           1 :     if (!aosOptions.FetchNameValue("BLOCKYSIZE"))
    2063             :     {
    2064           1 :         aosOptions.SetNameValue("BLOCKYSIZE", CPLString().Printf("%d", nY));
    2065             :     }
    2066           1 : }
    2067             : 
    2068             : /************************************************************************/
    2069             : /*                              CreateLL()                              */
    2070             : /*                                                                      */
    2071             : /*      Shared functionality between TileDBDataset::Create() and        */
    2072             : /*      TileDBDataset::CreateCopy() for creating TileDB array based on  */
    2073             : /*      a set of options and a configuration.                           */
    2074             : /************************************************************************/
    2075             : 
    2076         120 : TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
    2077             :                                                    int nXSize, int nYSize,
    2078             :                                                    int nBandsIn,
    2079             :                                                    GDALDataType eType,
    2080             :                                                    CSLConstList papszOptions)
    2081             : {
    2082             :     try
    2083             :     {
    2084         120 :         if ((nXSize <= 0 && nYSize <= 0))
    2085             :         {
    2086           0 :             return nullptr;
    2087             :         }
    2088             : 
    2089         240 :         auto poDS = std::make_unique<TileDBRasterDataset>();
    2090         120 :         poDS->nRasterXSize = nXSize;
    2091         120 :         poDS->nRasterYSize = nYSize;
    2092         120 :         poDS->eDataType = eType;
    2093         120 :         poDS->nBands = nBandsIn;
    2094         120 :         poDS->eAccess = GA_Update;
    2095             : 
    2096         120 :         if (poDS->nBands == 0)  // subdatasets
    2097             :         {
    2098           1 :             poDS->eIndexMode = ATTRIBUTES;
    2099             :         }
    2100             :         else
    2101             :         {
    2102             :             const char *pszIndexMode =
    2103         119 :                 CSLFetchNameValue(papszOptions, "INTERLEAVE");
    2104             : 
    2105         119 :             if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
    2106           0 :                 return nullptr;
    2107             :         }
    2108             : 
    2109             :         const char *pszConfig =
    2110         120 :             CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
    2111         120 :         if (pszConfig != nullptr)
    2112             :         {
    2113           0 :             poDS->m_osConfigFilename = pszConfig;
    2114           0 :             tiledb::Config cfg(pszConfig);
    2115           0 :             poDS->m_ctx.reset(new tiledb::Context(cfg));
    2116             :         }
    2117             :         else
    2118             :         {
    2119         120 :             poDS->m_ctx.reset(new tiledb::Context());
    2120             :         }
    2121             : 
    2122         120 :         if (CPLTestBool(
    2123             :                 CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "YES")))
    2124             :         {
    2125         110 :             poDS->m_bDatasetInGroup = true;
    2126         116 :             tiledb::create_group(*(poDS->m_ctx.get()), pszFilename);
    2127             : 
    2128             :             {
    2129         107 :                 tiledb::Group group(*(poDS->m_ctx.get()), pszFilename,
    2130         428 :                                     TILEDB_WRITE);
    2131         107 :                 group.put_metadata(
    2132             :                     DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    2133             :                     static_cast<int>(strlen(RASTER_DATASET_TYPE)),
    2134             :                     RASTER_DATASET_TYPE);
    2135             : 
    2136         107 :                 group.close();
    2137             :             }
    2138             : 
    2139             :             // Full resolution raster array
    2140         107 :             poDS->m_osArrayURI =
    2141         214 :                 CPLFormFilenameSafe(pszFilename, "l_0", nullptr);
    2142             :         }
    2143             :         else
    2144             :         {
    2145          10 :             poDS->m_osArrayURI = pszFilename;
    2146             :         }
    2147             : 
    2148             :         const char *pszCompression =
    2149         117 :             CSLFetchNameValue(papszOptions, "COMPRESSION");
    2150             :         const char *pszCompressionLevel =
    2151         117 :             CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
    2152             : 
    2153             :         const char *pszBlockXSize =
    2154         117 :             CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
    2155         117 :         poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
    2156             :         const char *pszBlockYSize =
    2157         117 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
    2158         117 :         poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
    2159         117 :         poDS->bStats = CSLFetchBoolean(papszOptions, "STATS", FALSE);
    2160             : 
    2161             :         const char *pszTimestamp =
    2162         117 :             CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
    2163         117 :         if (pszTimestamp != nullptr)
    2164           2 :             poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
    2165             : 
    2166             :         // set dimensions and attribute type for schema
    2167         234 :         poDS->m_schema.reset(
    2168         117 :             new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
    2169         117 :         poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
    2170         117 :         poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
    2171             : 
    2172         117 :         poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
    2173             : 
    2174         117 :         if (pszCompression != nullptr)
    2175             :         {
    2176           0 :             int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
    2177           0 :             if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
    2178           0 :                                          *(poDS->m_filterList.get()),
    2179           0 :                                          pszCompression, nLevel) == CE_None)
    2180             :             {
    2181           0 :                 poDS->SetMetadataItem("COMPRESSION", pszCompression,
    2182           0 :                                       "IMAGE_STRUCTURE");
    2183           0 :                 poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
    2184             :             }
    2185             :         }
    2186             : 
    2187         234 :         CPLString osAux = CPLGetBasenameSafe(pszFilename);
    2188         117 :         osAux += ".tdb";
    2189             : 
    2190         234 :         poDS->SetPhysicalFilename(
    2191         234 :             CPLFormFilenameSafe(pszFilename, osAux.c_str(), nullptr).c_str());
    2192             : 
    2193             :         // Initialize PAM information.
    2194         117 :         poDS->SetDescription(pszFilename);
    2195             : 
    2196             :         // Note the dimension bounds are inclusive and are expanded to the match
    2197             :         // the block size
    2198         117 :         poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
    2199         117 :         poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
    2200             : 
    2201             :         // register additional attributes to the pixel value, these will be
    2202             :         // be reported as subdatasets on future reads
    2203             :         CSLConstList papszAttributes =
    2204         117 :             CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
    2205         121 :         for (const char *pszAttribute : cpl::Iterate(papszAttributes))
    2206             :         {
    2207             :             // modeling additional attributes as subdatasets
    2208           4 :             poDS->m_bHasSubDatasets = true;
    2209             :             // check each attribute is a GDAL source
    2210             :             std::unique_ptr<GDALDataset> poAttrDS(
    2211           8 :                 GDALDataset::Open(pszAttribute, GA_ReadOnly));
    2212             : 
    2213           4 :             if (poAttrDS != nullptr)
    2214             :             {
    2215             :                 // check each is co-registered
    2216             :                 // candidate band
    2217           4 :                 int nAttrBands = poAttrDS->GetRasterCount();
    2218           4 :                 if (nAttrBands > 0)
    2219             :                 {
    2220           4 :                     GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
    2221             : 
    2222           4 :                     if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
    2223           8 :                         (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
    2224           4 :                         (poDS->nBands == nAttrBands))
    2225             :                     {
    2226             :                         // could check geotransform, but it is sufficient
    2227             :                         // that cartesian dimensions are equal
    2228           4 :                         poDS->m_lpoAttributeDS.push_back(std::move(poAttrDS));
    2229             :                     }
    2230             :                     else
    2231             :                     {
    2232           0 :                         CPLError(
    2233             :                             CE_Warning, CPLE_AppDefined,
    2234             :                             "Skipping %s as it has a different dimension\n",
    2235             :                             pszAttribute);
    2236             :                     }
    2237             :                 }
    2238             :                 else
    2239             :                 {
    2240           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2241             :                              "Skipping %s as it doesn't have any bands\n",
    2242             :                              pszAttribute);
    2243             :                 }
    2244             :             }
    2245             :             else
    2246             :             {
    2247           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2248             :                          "Skipping %s, not recognized as a GDAL dataset\n",
    2249             :                          pszAttribute);
    2250             :             }
    2251             :         }
    2252             : 
    2253         117 :         return poDS.release();
    2254             :     }
    2255           3 :     catch (const tiledb::TileDBError &e)
    2256             :     {
    2257           3 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
    2258           3 :         return nullptr;
    2259             :     }
    2260             : }
    2261             : 
    2262             : /************************************************************************/
    2263             : /*                          DeferredCreate()                            */
    2264             : /*                                                                      */
    2265             : /*  Create dimension, domains and attributes. and optionally the array  */
    2266             : /************************************************************************/
    2267             : 
    2268         120 : bool TileDBRasterDataset::DeferredCreate(bool bCreateArray)
    2269             : {
    2270         120 :     CPLAssert(!m_bDeferredCreateHasRun);
    2271         120 :     m_bDeferredCreateHasRun = true;
    2272         120 :     m_bDeferredCreateHasBeenSuccessful = false;
    2273             : 
    2274             :     try
    2275             :     {
    2276             :         // this driver enforces that all subdatasets are the same size
    2277         240 :         tiledb::Domain domain(*m_ctx);
    2278             : 
    2279         120 :         const uint64_t w = static_cast<uint64_t>(nBlocksX) * nBlockXSize - 1;
    2280         120 :         const uint64_t h = static_cast<uint64_t>(nBlocksY) * nBlockYSize - 1;
    2281             : 
    2282         120 :         auto d1 = tiledb::Dimension::create<uint64_t>(*m_ctx, "X", {0, w},
    2283         483 :                                                       uint64_t(nBlockXSize));
    2284         117 :         auto d2 = tiledb::Dimension::create<uint64_t>(*m_ctx, "Y", {0, h},
    2285         468 :                                                       uint64_t(nBlockYSize));
    2286             : 
    2287             :         {
    2288             :             CPLErr eErr;
    2289             :             // Only used for unit test purposes (to check ability of GDAL to read
    2290             :             // an arbitrary array)
    2291             :             const char *pszAttrName =
    2292         117 :                 CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    2293         117 :             if ((nBands == 0) || (eIndexMode == ATTRIBUTES))
    2294             :             {
    2295           6 :                 eErr = AddDimensions(domain, pszAttrName, d2, d1, nullptr);
    2296             :             }
    2297             :             else
    2298             :             {
    2299             :                 auto d3 = tiledb::Dimension::create<uint64_t>(
    2300         222 :                     *m_ctx, "BANDS", {1, uint64_t(nBands)}, 1);
    2301         111 :                 eErr = AddDimensions(domain, pszAttrName, d2, d1, &d3);
    2302             :             }
    2303         117 :             if (eErr != CE_None)
    2304           0 :                 return false;
    2305             :         }
    2306             : 
    2307         117 :         m_schema->set_domain(domain).set_order(
    2308         117 :             {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
    2309             : 
    2310             :         // register additional attributes to the pixel value, these will be
    2311             :         // be reported as subdatasets on future reads
    2312         121 :         for (const auto &poAttrDS : m_lpoAttributeDS)
    2313             :         {
    2314             :             const std::string osAttrName =
    2315           4 :                 CPLGetBasenameSafe(poAttrDS->GetDescription());
    2316           4 :             GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
    2317           4 :             int bHasNoData = false;
    2318           4 :             const double dfNoData = poAttrBand->GetNoDataValue(&bHasNoData);
    2319           4 :             CreateAttribute(poAttrBand->GetRasterDataType(), osAttrName.c_str(),
    2320           4 :                             1, CPL_TO_BOOL(bHasNoData), dfNoData);
    2321             :         }
    2322             : 
    2323         117 :         if (bCreateArray)
    2324             :         {
    2325         116 :             CreateArray();
    2326             :         }
    2327             : 
    2328         117 :         m_bDeferredCreateHasBeenSuccessful = true;
    2329         117 :         return true;
    2330             :     }
    2331           3 :     catch (const tiledb::TileDBError &e)
    2332             :     {
    2333           3 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
    2334           3 :         return false;
    2335             :     }
    2336             : }
    2337             : 
    2338             : /************************************************************************/
    2339             : /*                          CreateArray()                               */
    2340             : /************************************************************************/
    2341             : 
    2342         117 : void TileDBRasterDataset::CreateArray()
    2343             : {
    2344         117 :     tiledb::Array::create(m_osArrayURI, *m_schema);
    2345             : 
    2346         117 :     if (m_bDatasetInGroup)
    2347             :     {
    2348         321 :         tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    2349         107 :         group.add_member(m_osArrayURI, false);
    2350         107 :         group.close();
    2351             :     }
    2352             : 
    2353         117 :     if (nTimestamp)
    2354           4 :         m_array.reset(new tiledb::Array(
    2355           2 :             *m_ctx, m_osArrayURI, TILEDB_WRITE,
    2356           6 :             tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
    2357             :     else
    2358         115 :         m_array.reset(new tiledb::Array(*m_ctx, m_osArrayURI, TILEDB_WRITE));
    2359         117 : }
    2360             : 
    2361             : /************************************************************************/
    2362             : /*                              CopySubDatasets()                       */
    2363             : /*                                                                      */
    2364             : /*      Copy SubDatasets from src to a TileDBRasterDataset              */
    2365             : /*                                                                      */
    2366             : /************************************************************************/
    2367             : 
    2368           1 : CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
    2369             :                                             TileDBRasterDataset *poDstDS,
    2370             :                                             GDALProgressFunc pfnProgress,
    2371             :                                             void *pProgressData)
    2372             : 
    2373             : {
    2374             :     try
    2375             :     {
    2376           2 :         std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
    2377           1 :         poDstDS->m_bHasSubDatasets = true;
    2378           1 :         CSLConstList papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
    2379           1 :         if (!papszSrcSubDatasets)
    2380           0 :             return CE_Failure;
    2381             :         const char *pszSubDSName =
    2382           1 :             CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    2383           1 :         if (!pszSubDSName)
    2384           0 :             return CE_Failure;
    2385             : 
    2386             :         CPLStringList apszTokens(CSLTokenizeString2(
    2387           2 :             pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
    2388             :         // FIXME? this is tailored for HDF5-like subdataset names
    2389             :         // HDF5:foo.hdf5:attrname.
    2390           1 :         if (apszTokens.size() != 3)
    2391             :         {
    2392           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2393             :                      "Cannot guess attribute name in %s", pszSubDSName);
    2394           0 :             return CE_Failure;
    2395             :         }
    2396             : 
    2397             :         std::unique_ptr<GDALDataset> poSubDataset(
    2398           2 :             GDALDataset::Open(pszSubDSName));
    2399           2 :         if (poSubDataset.get() == nullptr ||
    2400           1 :             poSubDataset->GetRasterCount() == 0)
    2401             :         {
    2402           0 :             return CE_Failure;
    2403             :         }
    2404             : 
    2405           1 :         uint64_t nSubXSize = poSubDataset->GetRasterXSize();
    2406           1 :         uint64_t nSubYSize = poSubDataset->GetRasterYSize();
    2407             : 
    2408           1 :         const char *pszAttrName = apszTokens[2];
    2409             : 
    2410           1 :         auto poFirstSubDSBand = poSubDataset->GetRasterBand(1);
    2411           1 :         int bFirstSubDSBandHasNoData = FALSE;
    2412             :         const double dfFirstSubDSBandNoData =
    2413           1 :             poFirstSubDSBand->GetNoDataValue(&bFirstSubDSBandHasNoData);
    2414           1 :         poDstDS->CreateAttribute(poFirstSubDSBand->GetRasterDataType(),
    2415             :                                  pszAttrName, poSubDataset->GetRasterCount(),
    2416           1 :                                  CPL_TO_BOOL(bFirstSubDSBandHasNoData),
    2417             :                                  dfFirstSubDSBandNoData);
    2418           1 :         apoDatasets.push_back(std::move(poSubDataset));
    2419             : 
    2420           8 :         for (const auto &[pszKey, pszValue] :
    2421           9 :              cpl::IterateNameValue(papszSrcSubDatasets))
    2422             :         {
    2423           4 :             if (EQUAL(pszKey, "SUBDATASET_1_NAME") || !strstr(pszKey, "_NAME"))
    2424             :             {
    2425           3 :                 continue;
    2426             :             }
    2427           1 :             pszSubDSName = pszValue;
    2428             :             apszTokens = CSLTokenizeString2(
    2429           1 :                 pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
    2430           1 :             if (apszTokens.size() != 3)
    2431             :             {
    2432           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2433             :                          "Cannot guess attribute name in %s", pszSubDSName);
    2434           0 :                 continue;
    2435             :             }
    2436             : 
    2437             :             std::unique_ptr<GDALDataset> poSubDS(
    2438           2 :                 GDALDataset::Open(pszSubDSName));
    2439           1 :             if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
    2440             :             {
    2441           1 :                 GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
    2442             :                 int nBlockXSize, nBlockYSize;
    2443           1 :                 poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2444             : 
    2445           1 :                 int bHasNoData = FALSE;
    2446           1 :                 const double dfNoData = poBand->GetNoDataValue(&bHasNoData);
    2447             : 
    2448           1 :                 if ((poSubDS->GetRasterXSize() !=
    2449           1 :                      static_cast<int>(nSubXSize)) ||
    2450           1 :                     (poSubDS->GetRasterYSize() !=
    2451           1 :                      static_cast<int>(nSubYSize)) ||
    2452           3 :                     (nBlockXSize != poDstDS->nBlockXSize) ||
    2453           1 :                     (nBlockYSize != poDstDS->nBlockYSize))
    2454             :                 {
    2455           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2456             :                              "Sub-datasets must have the same dimension,"
    2457             :                              " and block sizes, skipping %s",
    2458             :                              pszSubDSName);
    2459             :                 }
    2460             :                 else
    2461             :                 {
    2462           1 :                     pszAttrName = apszTokens[2];
    2463           1 :                     poDstDS->CreateAttribute(
    2464             :                         poSubDS->GetRasterBand(1)->GetRasterDataType(),
    2465             :                         pszAttrName, poSubDS->GetRasterCount(),
    2466           1 :                         CPL_TO_BOOL(bHasNoData), dfNoData);
    2467           1 :                     apoDatasets.push_back(std::move(poSubDS));
    2468             :                 }
    2469             :             }
    2470             :             else
    2471             :             {
    2472           0 :                 CPLError(
    2473             :                     CE_Warning, CPLE_AppDefined,
    2474             :                     "Sub-datasets must be not null and contain data in bands,"
    2475             :                     "skipping %s\n",
    2476             :                     pszSubDSName);
    2477             :             }
    2478             :         }
    2479             : 
    2480           1 :         poDstDS->SetMetadata(poDstDS->m_aosSubdatasetMD.List(), "SUBDATASETS");
    2481             : 
    2482           1 :         poDstDS->CreateArray();
    2483             : 
    2484             :         /* --------------------------------------------------------  */
    2485             :         /*      Report preliminary (0) progress.                     */
    2486             :         /* --------------------------------------------------------- */
    2487           1 :         if (!pfnProgress(0.0, nullptr, pProgressData))
    2488             :         {
    2489           0 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    2490             :                      "User terminated CreateCopy()");
    2491           0 :             return CE_Failure;
    2492             :         }
    2493             : 
    2494             :         // copy over subdatasets by block
    2495           2 :         tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
    2496           1 :         query.set_layout(TILEDB_GLOBAL_ORDER);
    2497           1 :         const uint64_t nTotalBlocks =
    2498           1 :             static_cast<uint64_t>(poDstDS->nBlocksX) * poDstDS->nBlocksY;
    2499           1 :         uint64_t nBlockCounter = 0;
    2500             : 
    2501             :         // row-major
    2502           6 :         for (int j = 0; j < poDstDS->nBlocksY; ++j)
    2503             :         {
    2504          55 :             for (int i = 0; i < poDstDS->nBlocksX; ++i)
    2505             :             {
    2506          50 :                 std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
    2507             :                 // have to write set all tiledb attributes on write
    2508          50 :                 int iAttr = 0;
    2509         150 :                 for (auto &poSubDS : apoDatasets)
    2510             :                 {
    2511             :                     const GDALDataType eDT =
    2512         100 :                         poSubDS->GetRasterBand(1)->GetRasterDataType();
    2513             : 
    2514         200 :                     for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
    2515             :                     {
    2516         100 :                         const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
    2517         100 :                         const size_t nValues =
    2518         100 :                             static_cast<size_t>(poDstDS->nBlockXSize) *
    2519         100 :                             poDstDS->nBlockYSize;
    2520         100 :                         void *pBlock = VSI_MALLOC_VERBOSE(nDTSize * nValues);
    2521         100 :                         if (!pBlock)
    2522           0 :                             return CE_Failure;
    2523         100 :                         aBlocks.emplace_back(pBlock, &VSIFree);
    2524         100 :                         GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
    2525         100 :                         if (poBand->ReadBlock(i, j, pBlock) == CE_None)
    2526             :                         {
    2527         100 :                             SetBuffer(
    2528             :                                 &query, eDT,
    2529         200 :                                 poDstDS->m_schema->attribute(iAttr).name(),
    2530             :                                 pBlock, nValues);
    2531             :                         }
    2532         100 :                         ++iAttr;
    2533             :                     }
    2534             :                 }
    2535             : 
    2536          50 :                 if (poDstDS->bStats)
    2537           0 :                     tiledb::Stats::enable();
    2538             : 
    2539          50 :                 auto status = query.submit();
    2540             : 
    2541          50 :                 if (poDstDS->bStats)
    2542             :                 {
    2543           0 :                     tiledb::Stats::dump(stdout);
    2544           0 :                     tiledb::Stats::disable();
    2545             :                 }
    2546             : 
    2547          50 :                 if (status == tiledb::Query::Status::FAILED)
    2548             :                 {
    2549           0 :                     return CE_Failure;
    2550             :                 }
    2551             : 
    2552          50 :                 ++nBlockCounter;
    2553          50 :                 if (!pfnProgress(static_cast<double>(nBlockCounter) /
    2554          50 :                                      static_cast<double>(nTotalBlocks),
    2555             :                                  nullptr, pProgressData))
    2556             :                 {
    2557           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt,
    2558             :                              "User terminated CreateCopy()");
    2559           0 :                     return CE_Failure;
    2560             :                 }
    2561             :             }
    2562             :         }
    2563             : 
    2564           1 :         query.finalize();
    2565             : 
    2566           1 :         return CE_None;
    2567             :     }
    2568           0 :     catch (const tiledb::TileDBError &e)
    2569             :     {
    2570           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2571           0 :         return CE_Failure;
    2572             :     }
    2573             : }
    2574             : 
    2575             : /************************************************************************/
    2576             : /*                              Create()                                */
    2577             : /************************************************************************/
    2578             : 
    2579         119 : TileDBRasterDataset *TileDBRasterDataset::Create(const char *pszFilename,
    2580             :                                                  int nXSize, int nYSize,
    2581             :                                                  int nBandsIn,
    2582             :                                                  GDALDataType eType,
    2583             :                                                  char **papszOptions)
    2584             : 
    2585             : {
    2586         238 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2587             : 
    2588             :     std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
    2589         238 :         osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
    2590             : 
    2591         119 :     if (!poDS)
    2592           3 :         return nullptr;
    2593             : 
    2594             :     const char *pszAttrName =
    2595         116 :         CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    2596         314 :     for (int i = 0; i < poDS->nBands; i++)
    2597             :     {
    2598         198 :         if (poDS->eIndexMode == ATTRIBUTES)
    2599          24 :             poDS->SetBand(
    2600             :                 i + 1, new TileDBRasterBand(
    2601          12 :                            poDS.get(), i + 1,
    2602          24 :                            TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
    2603             :         else
    2604         558 :             poDS->SetBand(i + 1,
    2605         372 :                           new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
    2606             :     }
    2607             : 
    2608             :     // TILEDB_WRITE_IMAGE_STRUCTURE=NO only used for unit test purposes (to
    2609             :     // check ability of GDAL to read an arbitrary array)
    2610         116 :     if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
    2611             :     {
    2612         204 :         CPLStringList aosImageStruct;
    2613             :         aosImageStruct.SetNameValue(
    2614         102 :             "NBITS", CPLString().Printf("%d", poDS->nBitsPerSample));
    2615             :         aosImageStruct.SetNameValue(
    2616             :             "DATA_TYPE",
    2617         102 :             CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
    2618             :         aosImageStruct.SetNameValue(
    2619         102 :             "X_SIZE", CPLString().Printf("%d", poDS->nRasterXSize));
    2620             :         aosImageStruct.SetNameValue(
    2621         102 :             "Y_SIZE", CPLString().Printf("%d", poDS->nRasterYSize));
    2622             :         aosImageStruct.SetNameValue("INTERLEAVE",
    2623         102 :                                     index_type_name(poDS->eIndexMode));
    2624         102 :         aosImageStruct.SetNameValue("DATASET_TYPE", RASTER_DATASET_TYPE);
    2625             : 
    2626         102 :         if (poDS->m_lpoAttributeDS.size() > 0)
    2627             :         {
    2628           2 :             int i = 0;
    2629           6 :             for (auto const &poAttrDS : poDS->m_lpoAttributeDS)
    2630             :             {
    2631           4 :                 aosImageStruct.SetNameValue(
    2632           8 :                     CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
    2633          12 :                     CPLGetBasenameSafe(poAttrDS->GetDescription()).c_str());
    2634             :             }
    2635             :         }
    2636         102 :         poDS->SetMetadata(aosImageStruct.List(), "IMAGE_STRUCTURE");
    2637             :     }
    2638             : 
    2639         116 :     return poDS.release();
    2640             : }
    2641             : 
    2642             : /************************************************************************/
    2643             : /*                              CreateCopy()                            */
    2644             : /************************************************************************/
    2645             : 
    2646          52 : GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
    2647             :                                              GDALDataset *poSrcDS, int bStrict,
    2648             :                                              char **papszOptions,
    2649             :                                              GDALProgressFunc pfnProgress,
    2650             :                                              void *pProgressData)
    2651             : 
    2652             : {
    2653         104 :     CPLStringList aosOptions(CSLDuplicate(papszOptions));
    2654         104 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2655             : 
    2656          52 :     std::unique_ptr<TileDBRasterDataset> poDstDS;
    2657             : 
    2658          52 :     if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
    2659             :     {
    2660             :         // TileDB schemas are fixed
    2661           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2662             :                  "TileDB driver does not support "
    2663             :                  "appending to an existing schema.");
    2664           0 :         return nullptr;
    2665             :     }
    2666             : 
    2667          52 :     char **papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
    2668             : 
    2669          52 :     if (papszSrcSubDatasets == nullptr)
    2670             :     {
    2671          51 :         const int nBands = poSrcDS->GetRasterCount();
    2672             : 
    2673          51 :         if (nBands > 0)
    2674             :         {
    2675          51 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
    2676          51 :             GDALDataType eType = poBand->GetRasterDataType();
    2677             : 
    2678          85 :             for (int i = 2; i <= nBands; ++i)
    2679             :             {
    2680          34 :                 if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
    2681             :                 {
    2682           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2683             :                              "TileDB driver does not support "
    2684             :                              "source dataset with different band data types.");
    2685           0 :                     return nullptr;
    2686             :                 }
    2687             :             }
    2688             : 
    2689          51 :             poDstDS.reset(TileDBRasterDataset::Create(
    2690             :                 osArrayPath, poSrcDS->GetRasterXSize(),
    2691             :                 poSrcDS->GetRasterYSize(), nBands, eType, papszOptions));
    2692             : 
    2693          51 :             if (!poDstDS)
    2694             :             {
    2695           3 :                 return nullptr;
    2696             :             }
    2697             : 
    2698         130 :             for (int i = 1; i <= nBands; ++i)
    2699             :             {
    2700          82 :                 int bHasNoData = FALSE;
    2701             :                 const double dfNoData =
    2702          82 :                     poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
    2703          82 :                 if (bHasNoData)
    2704           3 :                     poDstDS->GetRasterBand(i)->SetNoDataValue(dfNoData);
    2705             :             }
    2706             : 
    2707             :             CPLErr eErr =
    2708          48 :                 GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
    2709             :                                            pfnProgress, pProgressData);
    2710             : 
    2711          48 :             if (eErr != CE_None)
    2712             :             {
    2713           0 :                 CPLError(eErr, CPLE_AppDefined,
    2714             :                          "Error copying raster to TileDB.");
    2715           0 :                 return nullptr;
    2716             :             }
    2717             :         }
    2718             :         else
    2719             :         {
    2720           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2721             :                      "TileDB driver does not support "
    2722             :                      "source dataset with zero bands.");
    2723           0 :             return nullptr;
    2724             :         }
    2725             :     }
    2726             :     else
    2727             :     {
    2728           1 :         if (bStrict)
    2729             :         {
    2730           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2731             :                      "TileDB driver does not support copying "
    2732             :                      "subdatasets in strict mode.");
    2733           0 :             return nullptr;
    2734             :         }
    2735             : 
    2736           2 :         if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
    2737           1 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
    2738             :         {
    2739           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2740             :                      "Changing block size is not supported when copying "
    2741             :                      "subdatasets.");
    2742           0 :             return nullptr;
    2743             :         }
    2744             : 
    2745           1 :         const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
    2746             :         const int nMaxFiles =
    2747           1 :             atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
    2748             : 
    2749           1 :         aosOptions.SetNameValue("CREATE_GROUP", "NO");
    2750             : 
    2751           1 :         if (nSubDatasetCount <= nMaxFiles)
    2752             :         {
    2753             :             const char *pszSource =
    2754           1 :                 CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    2755           1 :             if (pszSource)
    2756             :             {
    2757             :                 std::unique_ptr<GDALDataset> poSubDataset(
    2758           1 :                     GDALDataset::Open(pszSource));
    2759           1 :                 if (poSubDataset && poSubDataset->GetRasterCount() > 0)
    2760             :                 {
    2761           1 :                     GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
    2762             : 
    2763           1 :                     TileDBRasterDataset::SetBlockSize(poBand, aosOptions);
    2764           1 :                     poDstDS.reset(TileDBRasterDataset::CreateLL(
    2765             :                         osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
    2766           1 :                         poBand->GetRasterDataType(), aosOptions.List()));
    2767             : 
    2768           1 :                     if (poDstDS)
    2769             :                     {
    2770           1 :                         if (!poDstDS->DeferredCreate(
    2771             :                                 /* bCreateArray = */ false))
    2772           0 :                             return nullptr;
    2773             : 
    2774           1 :                         if (TileDBRasterDataset::CopySubDatasets(
    2775             :                                 poSrcDS, poDstDS.get(), pfnProgress,
    2776           1 :                                 pProgressData) != CE_None)
    2777             :                         {
    2778           0 :                             poDstDS.reset();
    2779           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    2780             :                                      "Unable to copy subdatasets.");
    2781             :                         }
    2782             :                     }
    2783             :                 }
    2784             :             }
    2785             :         }
    2786             :         else
    2787             :         {
    2788           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2789             :                      "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
    2790             :         }
    2791             :     }
    2792             : 
    2793             :     // TODO Supporting mask bands is a possible future task
    2794          49 :     if (poDstDS != nullptr)
    2795             :     {
    2796          49 :         int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    2797          49 :         poDstDS->CloneInfo(poSrcDS, nCloneFlags);
    2798             : 
    2799          49 :         if (poDstDS->FlushCache(false) != CE_None)
    2800             :         {
    2801           0 :             CPLError(CE_Failure, CPLE_AppDefined, "FlushCache() failed");
    2802           0 :             return nullptr;
    2803             :         }
    2804             : 
    2805          49 :         return poDstDS.release();
    2806             :     }
    2807           0 :     return nullptr;
    2808             : }
    2809             : 
    2810             : /************************************************************************/
    2811             : /*                            LoadOverviews()                           */
    2812             : /************************************************************************/
    2813             : 
    2814          28 : void TileDBRasterDataset::LoadOverviews()
    2815             : {
    2816          28 :     if (m_bLoadOverviewsDone)
    2817          23 :         return;
    2818           5 :     m_bLoadOverviewsDone = true;
    2819             : 
    2820             :     // NOTE: read overview_model.rst for a high level explanation of overviews
    2821             :     // are stored.
    2822             : 
    2823           5 :     if (!m_bDatasetInGroup)
    2824           0 :         return;
    2825             : 
    2826           5 :     CPLStringList aosOpenOptions;
    2827           5 :     if (nTimestamp)
    2828             :     {
    2829             :         aosOpenOptions.SetNameValue("TILEDB_TIMESTAMP",
    2830           0 :                                     CPLSPrintf("%" PRIu64, nTimestamp));
    2831             :     }
    2832           5 :     if (!m_osConfigFilename.empty())
    2833             :     {
    2834             :         aosOpenOptions.SetNameValue("TILEDB_CONFIG",
    2835           0 :                                     m_osConfigFilename.c_str());
    2836             :     }
    2837          10 :     for (int i = 0; i < m_nOverviewCountFromMetadata; ++i)
    2838             :     {
    2839           5 :         const std::string osArrayName = CPLSPrintf("l_%d", 1 + i);
    2840             :         const std::string osOvrDatasetName =
    2841           5 :             CPLFormFilenameSafe(GetDescription(), osArrayName.c_str(), nullptr);
    2842             : 
    2843           5 :         GDALOpenInfo oOpenInfo(osOvrDatasetName.c_str(), eAccess);
    2844           5 :         oOpenInfo.papszOpenOptions = aosOpenOptions.List();
    2845             :         auto poOvrDS = std::unique_ptr<GDALDataset>(
    2846           5 :             Open(&oOpenInfo, tiledb::Object::Type::Array));
    2847           5 :         if (!poOvrDS)
    2848           0 :             return;
    2849           5 :         if (poOvrDS->GetRasterCount() != nBands)
    2850             :         {
    2851           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2852             :                      "Overview %s has not the same number of bands as full "
    2853             :                      "resolution dataset",
    2854             :                      osOvrDatasetName.c_str());
    2855           0 :             return;
    2856             :         }
    2857           5 :         m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    2858             :     }
    2859             : }
    2860             : 
    2861             : /************************************************************************/
    2862             : /*                            IBuildOverviews()                         */
    2863             : /************************************************************************/
    2864             : 
    2865          13 : CPLErr TileDBRasterDataset::IBuildOverviews(
    2866             :     const char *pszResampling, int nOverviews, const int *panOverviewList,
    2867             :     int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
    2868             :     void *pProgressData, CSLConstList papszOptions)
    2869             : {
    2870             :     // NOTE: read overview_model.rst for a high level explanation of overviews
    2871             :     // are stored.
    2872             : 
    2873          13 :     if (eAccess == GA_ReadOnly)
    2874             :     {
    2875           3 :         if (!CPLTestBool(
    2876             :                 CPLGetConfigOption("TILEDB_GEOTIFF_OVERVIEWS", "FALSE")))
    2877             :         {
    2878           2 :             ReportError(
    2879             :                 CE_Failure, CPLE_NotSupported,
    2880             :                 "Cannot %s overviews in TileDB format in read-only mode",
    2881             :                 nOverviews ? "create" : "delete");
    2882           2 :             return CE_Failure;
    2883             :         }
    2884             : 
    2885             :         // GeoTIFF overviews. This used to be supported before GDAL 3.10
    2886             :         // although likely not desirable.
    2887           1 :         return GDALPamDataset::IBuildOverviews(
    2888             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    2889           1 :             pfnProgress, pProgressData, papszOptions);
    2890             :     }
    2891             : 
    2892          10 :     if (nBands == 0)
    2893             :     {
    2894           0 :         return CE_Failure;
    2895             :     }
    2896             : 
    2897             :     // If we already have PAM overview (i.e. GeoTIFF based), go through PAM
    2898          10 :     if (cpl::down_cast<GDALPamRasterBand *>(GetRasterBand(1))
    2899          10 :             ->GDALPamRasterBand::GetOverviewCount() > 0)
    2900             :     {
    2901           1 :         return GDALPamDataset::IBuildOverviews(
    2902             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    2903           1 :             pfnProgress, pProgressData, papszOptions);
    2904             :     }
    2905             : 
    2906           9 :     if (!m_bDatasetInGroup)
    2907             :     {
    2908           2 :         ReportError(CE_Failure, CPLE_NotSupported,
    2909             :                     "IBuildOverviews() only supported for datasets created "
    2910             :                     "with CREATE_GROUP=YES");
    2911           2 :         return CE_Failure;
    2912             :     }
    2913             : 
    2914             :     /* -------------------------------------------------------------------- */
    2915             :     /*      Our overview support currently only works safely if all         */
    2916             :     /*      bands are handled at the same time.                             */
    2917             :     /* -------------------------------------------------------------------- */
    2918           7 :     if (nListBands != nBands)
    2919             :     {
    2920           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    2921             :                     "Generation of TileDB overviews currently only "
    2922             :                     "supported when operating on all bands.  "
    2923             :                     "Operation failed.");
    2924           0 :         return CE_Failure;
    2925             :     }
    2926             : 
    2927             :     // Force loading existing overviews
    2928           7 :     if (m_nOverviewCountFromMetadata)
    2929           4 :         GetRasterBand(1)->GetOverviewCount();
    2930           7 :     m_bLoadOverviewsDone = true;
    2931             : 
    2932             :     /* -------------------------------------------------------------------- */
    2933             :     /*      Deletes existing overviews if requested.                        */
    2934             :     /* -------------------------------------------------------------------- */
    2935           7 :     if (nOverviews == 0)
    2936             :     {
    2937           2 :         CPLErr eErr = CE_None;
    2938             : 
    2939             :         // Unlink arrays from he group
    2940             :         try
    2941             :         {
    2942           6 :             tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    2943           4 :             for (auto &&poODS : m_apoOverviewDS)
    2944             :             {
    2945           2 :                 group.remove_member(poODS->GetDescription());
    2946             :             }
    2947           2 :             group.close();
    2948             :         }
    2949           0 :         catch (const tiledb::TileDBError &e)
    2950             :         {
    2951           0 :             eErr = CE_Failure;
    2952           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2953             :         }
    2954             : 
    2955           2 :         tiledb::VFS vfs(*m_ctx, m_ctx->config());
    2956             : 
    2957             :         // Delete arrays
    2958           4 :         for (auto &&poODS : m_apoOverviewDS)
    2959             :         {
    2960             :             try
    2961             :             {
    2962           2 :                 CPL_IGNORE_RET_VAL(poODS->Close());
    2963           2 :                 tiledb::Array::delete_array(*m_ctx, poODS->GetDescription());
    2964           2 :                 if (vfs.is_dir(poODS->GetDescription()))
    2965             :                 {
    2966           2 :                     vfs.remove_dir(poODS->GetDescription());
    2967             :                 }
    2968             :             }
    2969           0 :             catch (const tiledb::TileDBError &e)
    2970             :             {
    2971           0 :                 eErr = CE_Failure;
    2972           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2973             :                          "Array::delete_array(%s) failed: %s",
    2974           0 :                          poODS->GetDescription(), e.what());
    2975             :             }
    2976           2 :             m_apoOverviewDSRemoved.emplace_back(std::move(poODS));
    2977             :         }
    2978             : 
    2979           2 :         m_apoOverviewDS.clear();
    2980           2 :         m_nOverviewCountFromMetadata = 0;
    2981           2 :         MarkPamDirty();
    2982           2 :         return eErr;
    2983             :     }
    2984             : 
    2985             :     /* -------------------------------------------------------------------- */
    2986             :     /*      Establish which of the overview levels we already have, and     */
    2987             :     /*      which are new.                                                  */
    2988             :     /* -------------------------------------------------------------------- */
    2989          10 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    2990          10 :     for (int i = 0; i < nOverviews; ++i)
    2991             :     {
    2992           5 :         const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    2993           5 :         const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    2994             : 
    2995           6 :         for (const auto &poODS : m_apoOverviewDS)
    2996             :         {
    2997             :             const int nOvFactor =
    2998           2 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    2999             :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3000             : 
    3001             :             // If we already have a 1x1 overview and this new one would result
    3002             :             // in it too, then don't create it.
    3003           2 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3004           2 :                 nOXSize == 1 && nOYSize == 1)
    3005             :             {
    3006           0 :                 abRequireNewOverview[i] = false;
    3007           0 :                 break;
    3008             :             }
    3009             : 
    3010           3 :             if (nOvFactor == panOverviewList[i] ||
    3011           1 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3012             :                                                 GetRasterXSize(),
    3013             :                                                 GetRasterYSize()))
    3014             :             {
    3015           1 :                 abRequireNewOverview[i] = false;
    3016           1 :                 break;
    3017             :             }
    3018             :         }
    3019             : 
    3020           5 :         if (abRequireNewOverview[i])
    3021             :         {
    3022           4 :             CPLStringList aosCreationOptions;
    3023           4 :             aosCreationOptions.SetNameValue("CREATE_GROUP", "NO");
    3024             :             aosCreationOptions.SetNameValue(
    3025           4 :                 "NBITS", CPLString().Printf("%d", nBitsPerSample));
    3026             :             aosCreationOptions.SetNameValue("INTERLEAVE",
    3027           4 :                                             index_type_name(eIndexMode));
    3028           4 :             if (nTimestamp)
    3029             :             {
    3030             :                 aosCreationOptions.SetNameValue(
    3031           0 :                     "TILEDB_TIMESTAMP", CPLSPrintf("%" PRIu64, nTimestamp));
    3032             :             }
    3033           4 :             if (!m_osConfigFilename.empty())
    3034             :             {
    3035             :                 aosCreationOptions.SetNameValue("TILEDB_CONFIG",
    3036           0 :                                                 m_osConfigFilename.c_str());
    3037             :             }
    3038             : 
    3039             :             const std::string osArrayName =
    3040           4 :                 CPLSPrintf("l_%d", 1 + int(m_apoOverviewDS.size()));
    3041             :             const std::string osOvrDatasetName = CPLFormFilenameSafe(
    3042           4 :                 GetDescription(), osArrayName.c_str(), nullptr);
    3043             : 
    3044             :             auto poOvrDS = std::unique_ptr<TileDBRasterDataset>(
    3045             :                 Create(osOvrDatasetName.c_str(), nOXSize, nOYSize, nBands,
    3046             :                        GetRasterBand(1)->GetRasterDataType(),
    3047           4 :                        aosCreationOptions.List()));
    3048           4 :             if (!poOvrDS)
    3049           0 :                 return CE_Failure;
    3050             : 
    3051             :             // Apply nodata from main dataset
    3052          16 :             for (int j = 0; j < nBands; ++j)
    3053             :             {
    3054          12 :                 int bHasNoData = FALSE;
    3055             :                 const double dfNoData =
    3056          12 :                     GetRasterBand(j + 1)->GetNoDataValue(&bHasNoData);
    3057          12 :                 if (bHasNoData)
    3058          12 :                     poOvrDS->GetRasterBand(j + 1)->SetNoDataValue(dfNoData);
    3059             :             }
    3060             : 
    3061             :             // Apply georeferencing from main dataset
    3062           4 :             poOvrDS->SetSpatialRef(GetSpatialRef());
    3063           4 :             GDALGeoTransform gt;
    3064           4 :             if (GetGeoTransform(gt) == CE_None)
    3065             :             {
    3066           4 :                 gt.Rescale(static_cast<double>(nRasterXSize) / nOXSize,
    3067           4 :                            static_cast<double>(nRasterYSize) / nOYSize);
    3068           4 :                 poOvrDS->SetGeoTransform(gt);
    3069             :             }
    3070             : 
    3071           4 :             poOvrDS->DeferredCreate(/* bCreateArray = */ true);
    3072             : 
    3073             :             try
    3074             :             {
    3075          12 :                 tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    3076           4 :                 group.add_member(osOvrDatasetName, false);
    3077           4 :                 group.close();
    3078             :             }
    3079           0 :             catch (const tiledb::TileDBError &e)
    3080             :             {
    3081           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    3082             :             }
    3083             : 
    3084           4 :             m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    3085             :         }
    3086             :     }
    3087             : 
    3088           5 :     m_nOverviewCountFromMetadata = static_cast<int>(m_apoOverviewDS.size());
    3089           5 :     MarkPamDirty();
    3090             : 
    3091             :     /* -------------------------------------------------------------------- */
    3092             :     /*      Refresh/generate overviews that are listed.                     */
    3093             :     /* -------------------------------------------------------------------- */
    3094          10 :     std::vector<GDALRasterBand *> apoSrcBands;
    3095          10 :     std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands;
    3096           5 :     CPLErr eErr = CE_None;
    3097             :     const auto osNormalizedResampling =
    3098           5 :         GDALGetNormalizedOvrResampling(pszResampling);
    3099          20 :     for (int iBand = 0; eErr == CE_None && iBand < nBands; iBand++)
    3100             :     {
    3101          15 :         apoSrcBands.push_back(GetRasterBand(iBand + 1));
    3102          30 :         std::vector<GDALRasterBand *> apoOverviewBands;
    3103             : 
    3104             :         std::vector<bool> abAlreadyUsedOverviewBand(m_apoOverviewDS.size(),
    3105          30 :                                                     false);
    3106             : 
    3107          30 :         for (int i = 0; i < nOverviews; i++)
    3108             :         {
    3109          15 :             bool bFound = false;
    3110          18 :             for (size_t j = 0; j < m_apoOverviewDS.size(); ++j)
    3111             :             {
    3112          18 :                 if (!abAlreadyUsedOverviewBand[j])
    3113             :                 {
    3114          18 :                     auto &poODS = m_apoOverviewDS[j];
    3115          18 :                     int nOvFactor = GDALComputeOvFactor(
    3116             :                         poODS->GetRasterXSize(), nRasterXSize,
    3117             :                         poODS->GetRasterYSize(), nRasterYSize);
    3118             : 
    3119          21 :                     if (nOvFactor == panOverviewList[i] ||
    3120           3 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3121             :                                                         nRasterXSize,
    3122             :                                                         nRasterYSize))
    3123             :                     {
    3124          15 :                         abAlreadyUsedOverviewBand[j] = true;
    3125          15 :                         auto poOvrBand = poODS->GetRasterBand(iBand + 1);
    3126          15 :                         if (!osNormalizedResampling.empty())
    3127             :                         {
    3128             :                             // Store resampling method in band metadata, as it
    3129             :                             // can be used by the gdaladdo utilities to refresh
    3130             :                             // existing overviews with the method previously
    3131             :                             // used
    3132          15 :                             poOvrBand->SetMetadataItem(
    3133          15 :                                 "RESAMPLING", osNormalizedResampling.c_str());
    3134             :                         }
    3135          15 :                         apoOverviewBands.push_back(poOvrBand);
    3136          15 :                         bFound = true;
    3137          15 :                         break;
    3138             :                     }
    3139             :                 }
    3140             :             }
    3141          15 :             if (!bFound)
    3142             :             {
    3143           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3144             :                          "Could not find dataset corresponding to ov factor %d",
    3145           0 :                          panOverviewList[i]);
    3146           0 :                 eErr = CE_Failure;
    3147             :             }
    3148             :         }
    3149          15 :         if (iBand > 0)
    3150             :         {
    3151          10 :             CPLAssert(apoOverviewBands.size() == aapoOverviewBands[0].size());
    3152             :         }
    3153          15 :         aapoOverviewBands.emplace_back(std::move(apoOverviewBands));
    3154             :     }
    3155             : 
    3156           5 :     if (eErr == CE_None)
    3157             :     {
    3158           5 :         eErr = GDALRegenerateOverviewsMultiBand(apoSrcBands, aapoOverviewBands,
    3159             :                                                 pszResampling, pfnProgress,
    3160             :                                                 pProgressData, papszOptions);
    3161             :     }
    3162             : 
    3163           5 :     return eErr;
    3164             : }

Generated by: LCOV version 1.14