LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbdense.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1213 1480 82.0 %
Date: 2026-06-19 21:24:00 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             :     CPLErr IReadBlock(int, int, void *) override;
      47             :     CPLErr IWriteBlock(int, int, void *) override;
      48             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
      49             :                      GDALDataType, GSpacing, GSpacing,
      50             :                      GDALRasterIOExtraArg *psExtraArg) override;
      51             :     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_UInt8:
     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_UInt8;
     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'.");
     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.");
     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          71 : GDALColorInterp TileDBRasterBand::GetColorInterpretation()
     442             : 
     443             : {
     444          71 :     if (poGDS->nBands == 1)
     445          20 :         return GCI_GrayIndex;
     446             : 
     447          51 :     if (nBand == 1)
     448          20 :         return GCI_RedBand;
     449             : 
     450          31 :     else if (nBand == 2)
     451          15 :         return GCI_GreenBand;
     452             : 
     453          16 :     else if (nBand == 3)
     454          16 :         return GCI_BlueBand;
     455             : 
     456           0 :     return GCI_AlphaBand;
     457             : }
     458             : 
     459             : /************************************************************************/
     460             : /*                           GetNoDataValue()                           */
     461             : /************************************************************************/
     462             : 
     463          82 : double TileDBRasterBand::GetNoDataValue(int *pbHasNoData)
     464             : {
     465          82 :     if (pbHasNoData)
     466          78 :         *pbHasNoData = false;
     467          82 :     if (m_bNoDataSet)
     468             :     {
     469          11 :         if (pbHasNoData)
     470          11 :             *pbHasNoData = true;
     471          11 :         return m_dfNoData;
     472             :     }
     473          71 :     if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
     474           0 :         return 0.0;
     475          71 :     if (!poGDS->m_array)
     476           0 :         return 0.0;
     477          71 :     double dfNoData = 0.0;
     478             :     try
     479             :     {
     480          71 :         const void *value = nullptr;
     481          71 :         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          71 :         auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
     486          71 :                         ->schema()
     487         142 :                         .attribute(osAttrName);
     488          71 :         attr.get_fill_value(&value, &size);
     489          71 :         if (value &&
     490          71 :             size == static_cast<uint64_t>(GDALGetDataTypeSizeBytes(eDataType)))
     491             :         {
     492          71 :             switch (eDataType)
     493             :             {
     494          54 :                 case GDT_UInt8:
     495          54 :                     dfNoData = *static_cast<const uint8_t *>(value);
     496          54 :                     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          71 :             if (pbHasNoData)
     540          67 :                 *pbHasNoData = true;
     541             :         }
     542             :     }
     543           0 :     catch (const tiledb::TileDBError &e)
     544             :     {
     545           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     546             :     }
     547          71 :     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_UInt8:
     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(GDALProgressFunc, void *)
     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 : CSLConstList TileDBRasterDataset::GetMetadata(const char *pszDomain)
    1284             : 
    1285             : {
    1286         216 :     if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_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         129 :     poDS->bStats = CPL_TO_BOOL(
    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 :     CSLConstList papszStructMeta = poDS->GetMetadata(GDAL_MDD_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, GDALMD_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             :     const char *pszIndexMode =
    1531         129 :         CSLFetchNameValue(papszStructMeta, GDALMD_INTERLEAVE);
    1532             : 
    1533         129 :     if (pszIndexMode)
    1534         107 :         option_to_index_type(pszIndexMode, poDS->eIndexMode);
    1535             : 
    1536         258 :     std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
    1537             : 
    1538         129 :     int iYDim = 0;
    1539         129 :     int iXDim = 1;
    1540         129 :     if ((dims.size() == 2) || (dims.size() == 3))
    1541             :     {
    1542         129 :         if (dims.size() == 3)
    1543             :         {
    1544         141 :             if ((pszAttr != nullptr) &&
    1545         141 :                 (schema.attributes().count(pszAttr) == 0))
    1546             :             {
    1547           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1548             :                          "%s attribute is not found in TileDB schema.",
    1549             :                          pszAttr);
    1550           0 :                 return nullptr;
    1551             :             }
    1552             : 
    1553         123 :             if (poDS->eIndexMode == PIXEL)
    1554          15 :                 std::rotate(dims.begin(), dims.begin() + 2, dims.end());
    1555             : 
    1556         123 :             if (dims[0].type() != TILEDB_UINT64)
    1557             :             {
    1558           0 :                 const char *pszTypeName = "";
    1559           0 :                 tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1560           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1561             :                          "Unsupported BAND dimension type: %s", pszTypeName);
    1562           0 :                 return nullptr;
    1563             :             }
    1564         123 :             poDS->nBandStart = dims[0].domain<uint64_t>().first;
    1565         123 :             const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
    1566         246 :             if (nBandEnd < poDS->nBandStart ||
    1567         123 :                 nBandEnd - poDS->nBandStart >
    1568         123 :                     static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1569             :             {
    1570           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1571             :                          "Invalid bounds for BAND dimension.");
    1572           0 :                 return nullptr;
    1573             :             }
    1574         123 :             poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
    1575         123 :             iYDim = 1;
    1576         123 :             iXDim = 2;
    1577             :         }
    1578             :         else
    1579             :         {
    1580             :             const char *pszBands =
    1581           6 :                 poDS->GetMetadataItem("NUM_BANDS", GDAL_MDD_IMAGE_STRUCTURE);
    1582           6 :             if (pszBands)
    1583             :             {
    1584           1 :                 poDS->nBands = atoi(pszBands);
    1585             :             }
    1586             : 
    1587           6 :             poDS->eIndexMode = ATTRIBUTES;
    1588             :         }
    1589             :     }
    1590             :     else
    1591             :     {
    1592           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1593             :                  "Wrong number of dimensions %d: expected 2 or 3.",
    1594           0 :                  static_cast<int>(dims.size()));
    1595           0 :         return nullptr;
    1596             :     }
    1597             : 
    1598         258 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
    1599         129 :         !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
    1600             :     {
    1601           0 :         return nullptr;
    1602             :     }
    1603             : 
    1604         129 :     if (dims[iYDim].type() != TILEDB_UINT64)
    1605             :     {
    1606           0 :         const char *pszTypeName = "";
    1607           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1608           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1609             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1610           0 :         return nullptr;
    1611             :     }
    1612         129 :     if (!pszYSize)
    1613             :     {
    1614          16 :         const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
    1615          16 :         const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
    1616          32 :         if (nStart != 0 ||
    1617          16 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1618             :         {
    1619           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1620             :                      "Invalid bounds for Y dimension.");
    1621           0 :             return nullptr;
    1622             :         }
    1623          16 :         poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
    1624             :     }
    1625         129 :     const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
    1626         129 :     if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1627             :     {
    1628           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
    1629           0 :         return nullptr;
    1630             :     }
    1631         129 :     poDS->nBlockYSize = static_cast<int>(nBlockYSize);
    1632             : 
    1633         129 :     if (dims[iXDim].type() != TILEDB_UINT64)
    1634             :     {
    1635           0 :         const char *pszTypeName = "";
    1636           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1637           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1638             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1639           0 :         return nullptr;
    1640             :     }
    1641         129 :     if (!pszXSize)
    1642             :     {
    1643          16 :         const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
    1644          16 :         const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
    1645          32 :         if (nStart != 0 ||
    1646          16 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1647             :         {
    1648           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1649             :                      "Invalid bounds for X dimension.");
    1650           0 :             return nullptr;
    1651             :         }
    1652          16 :         poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
    1653             :     }
    1654         129 :     const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
    1655         129 :     if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1656             :     {
    1657           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
    1658           0 :         return nullptr;
    1659             :     }
    1660         129 :     poDS->nBlockXSize = static_cast<int>(nBlockXSize);
    1661             : 
    1662         129 :     poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
    1663         129 :     poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
    1664             : 
    1665         129 :     if (dims.size() == 3)
    1666             :     {
    1667             :         // Create band information objects.
    1668         844 :         for (int i = 1; i <= poDS->nBands; ++i)
    1669             :         {
    1670         721 :             if (pszAttr == nullptr)
    1671         189 :                 poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
    1672             :             else
    1673        1064 :                 poDS->SetBand(
    1674        1064 :                     i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
    1675             :         }
    1676             :     }
    1677             :     else  // subdatasets or only attributes
    1678             :     {
    1679           7 :         if ((poOpenInfo->eAccess == GA_Update) &&
    1680           1 :             (poDS->GetMetadata(GDAL_MDD_SUBDATASETS) != nullptr))
    1681             :         {
    1682           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1683             :                      "The TileDB driver does not support update access "
    1684             :                      "to subdatasets.");
    1685           0 :             return nullptr;
    1686             :         }
    1687             : 
    1688           6 :         if (!osSubdataset.empty())
    1689             :         {
    1690             :             // do we have the attribute in the schema
    1691           2 :             if (schema.attributes().count(osSubdataset))
    1692             :             {
    1693           0 :                 poDS->SetBand(
    1694           0 :                     1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
    1695             :             }
    1696             :             else
    1697             :             {
    1698           2 :                 if (schema.attributes().count(osSubdataset + "_1"))
    1699             :                 {
    1700             :                     // Create band information objects.
    1701           2 :                     for (int i = 1; i <= poDS->nBands; ++i)
    1702             :                     {
    1703           1 :                         CPLString osAttr = CPLString().Printf(
    1704           2 :                             "%s_%d", osSubdataset.c_str(), i);
    1705           2 :                         poDS->SetBand(
    1706           1 :                             i, new TileDBRasterBand(poDS.get(), i, osAttr));
    1707             :                     }
    1708             :                 }
    1709             :                 else
    1710             :                 {
    1711           1 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1712             :                              "%s attribute is not found in TileDB schema.",
    1713             :                              osSubdataset.c_str());
    1714           1 :                     return nullptr;
    1715             :                 }
    1716             :             }
    1717             :         }
    1718             :         else
    1719             :         {
    1720           4 :             CSLConstList papszMeta = poDS->GetMetadata(GDAL_MDD_SUBDATASETS);
    1721           4 :             if (papszMeta != nullptr)
    1722             :             {
    1723           1 :                 if ((CSLCount(papszMeta) / 2) == 1)
    1724             :                 {
    1725             :                     const char *pszSubDSName =
    1726           0 :                         poDS->m_aosSubdatasetMD.FetchNameValueDef(
    1727             :                             "SUBDATASET_1_NAME", "");
    1728           0 :                     GDALOpenInfo oOpenInfo(pszSubDSName, poOpenInfo->eAccess);
    1729           0 :                     return OpenInternal(&oOpenInfo, objectType);
    1730             :                 }
    1731             :             }
    1732           3 :             else if (poDS->eIndexMode == ATTRIBUTES)
    1733             :             {
    1734           3 :                 poDS->nBands = schema.attribute_num();
    1735             : 
    1736             :                 // Create band information objects.
    1737          11 :                 for (int i = 1; i <= poDS->nBands; ++i)
    1738             :                 {
    1739          16 :                     tiledb::Attribute const &attr = schema.attribute(i - 1);
    1740          16 :                     std::string const &attr_name = attr.name();
    1741             : 
    1742           8 :                     CPLDebug("TileDB", "Set attribute '%s' from TileDB schema",
    1743             :                              attr_name.c_str());
    1744             : 
    1745             :                     GDALRasterBand *band =
    1746           8 :                         new TileDBRasterBand(poDS.get(), i, attr_name.c_str());
    1747             : 
    1748           8 :                     poDS->SetBand(i, band);
    1749             :                 }
    1750             :             }
    1751             :             else
    1752             :             {
    1753           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1754             :                          "%s is missing required TileDB subdataset metadata.",
    1755             :                          osURI.c_str());
    1756           0 :                 return nullptr;
    1757             :             }
    1758             :         }
    1759             :     }
    1760             : 
    1761             :     // reload metadata now that bands are created to populate band metadata
    1762         128 :     poDS->TryLoadCachedXML(nullptr, false);
    1763             : 
    1764         256 :     tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
    1765             : 
    1766         128 :     if (!STARTS_WITH_CI(osURI, "TILEDB:") && vfs.is_dir(osURI))
    1767         128 :         poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
    1768             :     else
    1769           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1770             :                  "Overviews not supported for network writes.");
    1771             : 
    1772         128 :     return poDS.release();
    1773             : }
    1774             : 
    1775             : /************************************************************************/
    1776             : /*                          CreateAttribute()                           */
    1777             : /************************************************************************/
    1778             : 
    1779             : template <class T, class NoDataT = T>
    1780         129 : static tiledb::Attribute CreateAttribute(tiledb::Context &ctx,
    1781             :                                          const std::string &osAttrName,
    1782             :                                          tiledb::FilterList &filterList,
    1783             :                                          bool bHasFillValue, double dfFillValue)
    1784             : {
    1785         129 :     auto attr = tiledb::Attribute::create<T>(ctx, osAttrName, filterList);
    1786         129 :     if (bHasFillValue && GDALIsValueInRange<NoDataT>(dfFillValue))
    1787             :     {
    1788          30 :         const auto nVal = static_cast<NoDataT>(dfFillValue);
    1789          30 :         if (dfFillValue == static_cast<double>(nVal))
    1790             :         {
    1791             :             if constexpr (sizeof(T) == sizeof(NoDataT))
    1792             :             {
    1793          26 :                 attr.set_fill_value(&nVal, sizeof(nVal));
    1794             :             }
    1795             :             else
    1796             :             {
    1797           4 :                 T aVal = {nVal, nVal};
    1798           4 :                 attr.set_fill_value(&aVal, sizeof(aVal));
    1799             :             }
    1800             :         }
    1801             :     }
    1802         129 :     return attr;
    1803             : }
    1804             : 
    1805             : /************************************************************************/
    1806             : /*                          CreateAttribute()                           */
    1807             : /************************************************************************/
    1808             : 
    1809         123 : CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
    1810             :                                             const CPLString &osAttrName,
    1811             :                                             const int nSubRasterCount,
    1812             :                                             bool bHasFillValue,
    1813             :                                             double dfFillValue)
    1814             : {
    1815             :     try
    1816             :     {
    1817         252 :         for (int i = 0; i < nSubRasterCount; ++i)
    1818             :         {
    1819         129 :             CPLString osName(osAttrName);
    1820             :             // a few special cases
    1821             :             // remove any leading slashes or
    1822             :             // additional slashes as in the case of hdf5
    1823         129 :             if STARTS_WITH (osName, "//")
    1824             :             {
    1825           2 :                 osName = osName.substr(2);
    1826             :             }
    1827             : 
    1828         129 :             osName.replaceAll("/", "_");
    1829         129 :             CPLString osPrettyName = osName;
    1830             : 
    1831         129 :             if ((eIndexMode == ATTRIBUTES) ||
    1832         115 :                 ((m_bHasSubDatasets) && (nSubRasterCount > 1)))
    1833             :             {
    1834          14 :                 osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
    1835             :             }
    1836             : 
    1837         129 :             switch (eType)
    1838             :             {
    1839          53 :                 case GDT_UInt8:
    1840             :                 {
    1841         159 :                     m_schema->add_attribute(::CreateAttribute<unsigned char>(
    1842          53 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1843         106 :                         dfFillValue));
    1844          53 :                     nBitsPerSample = 8;
    1845          53 :                     break;
    1846             :                 }
    1847           4 :                 case GDT_Int8:
    1848             :                 {
    1849           4 :                     m_schema->add_attribute(
    1850           8 :                         ::CreateAttribute<int8_t>(*m_ctx, osName, *m_filterList,
    1851           8 :                                                   bHasFillValue, dfFillValue));
    1852           4 :                     nBitsPerSample = 8;
    1853           4 :                     break;
    1854             :                 }
    1855           5 :                 case GDT_UInt16:
    1856             :                 {
    1857          15 :                     m_schema->add_attribute(::CreateAttribute<uint16_t>(
    1858           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1859          10 :                         dfFillValue));
    1860           5 :                     nBitsPerSample = 16;
    1861           5 :                     break;
    1862             :                 }
    1863           5 :                 case GDT_UInt32:
    1864             :                 {
    1865          15 :                     m_schema->add_attribute(::CreateAttribute<uint32_t>(
    1866           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1867          10 :                         dfFillValue));
    1868           5 :                     nBitsPerSample = 32;
    1869           5 :                     break;
    1870             :                 }
    1871           4 :                 case GDT_UInt64:
    1872             :                 {
    1873          12 :                     m_schema->add_attribute(::CreateAttribute<uint64_t>(
    1874           4 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1875           8 :                         dfFillValue));
    1876           4 :                     nBitsPerSample = 64;
    1877           4 :                     break;
    1878             :                 }
    1879           5 :                 case GDT_Int16:
    1880             :                 {
    1881          15 :                     m_schema->add_attribute(::CreateAttribute<int16_t>(
    1882           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1883          10 :                         dfFillValue));
    1884           5 :                     nBitsPerSample = 16;
    1885           5 :                     break;
    1886             :                 }
    1887           7 :                 case GDT_Int32:
    1888             :                 {
    1889          21 :                     m_schema->add_attribute(::CreateAttribute<int32_t>(
    1890           7 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1891          14 :                         dfFillValue));
    1892           7 :                     nBitsPerSample = 32;
    1893           7 :                     break;
    1894             :                 }
    1895           4 :                 case GDT_Int64:
    1896             :                 {
    1897          12 :                     m_schema->add_attribute(::CreateAttribute<int64_t>(
    1898           4 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1899           8 :                         dfFillValue));
    1900           4 :                     nBitsPerSample = 64;
    1901           4 :                     break;
    1902             :                 }
    1903          12 :                 case GDT_Float32:
    1904             :                 {
    1905          12 :                     m_schema->add_attribute(
    1906          24 :                         ::CreateAttribute<float>(*m_ctx, osName, *m_filterList,
    1907          24 :                                                  bHasFillValue, dfFillValue));
    1908          12 :                     nBitsPerSample = 32;
    1909          12 :                     break;
    1910             :                 }
    1911           8 :                 case GDT_Float64:
    1912             :                 {
    1913           8 :                     m_schema->add_attribute(
    1914          16 :                         ::CreateAttribute<double>(*m_ctx, osName, *m_filterList,
    1915          16 :                                                   bHasFillValue, dfFillValue));
    1916           8 :                     nBitsPerSample = 64;
    1917           8 :                     break;
    1918             :                 }
    1919           5 :                 case GDT_CInt16:
    1920             :                 {
    1921           5 :                     m_schema->add_attribute(
    1922          10 :                         ::CreateAttribute<int16_t[2], int16_t>(
    1923           5 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1924          10 :                             dfFillValue));
    1925           5 :                     nBitsPerSample = 16;
    1926           5 :                     break;
    1927             :                 }
    1928           5 :                 case GDT_CInt32:
    1929             :                 {
    1930           5 :                     m_schema->add_attribute(
    1931          10 :                         ::CreateAttribute<int32_t[2], int32_t>(
    1932           5 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1933          10 :                             dfFillValue));
    1934           5 :                     nBitsPerSample = 32;
    1935           5 :                     break;
    1936             :                 }
    1937           5 :                 case GDT_CFloat32:
    1938             :                 {
    1939          15 :                     m_schema->add_attribute(::CreateAttribute<float[2], float>(
    1940           5 :                         *m_ctx, osName, *m_filterList, bHasFillValue,
    1941          10 :                         dfFillValue));
    1942           5 :                     nBitsPerSample = 32;
    1943           5 :                     break;
    1944             :                 }
    1945           7 :                 case GDT_CFloat64:
    1946             :                 {
    1947           7 :                     m_schema->add_attribute(
    1948          14 :                         ::CreateAttribute<double[2], double>(
    1949           7 :                             *m_ctx, osName, *m_filterList, bHasFillValue,
    1950          14 :                             dfFillValue));
    1951           7 :                     nBitsPerSample = 64;
    1952           7 :                     break;
    1953             :                 }
    1954           0 :                 default:
    1955           0 :                     return CE_Failure;
    1956             :             }
    1957             : 
    1958         129 :             if ((m_bHasSubDatasets) && (i == 0))
    1959             :             {
    1960           8 :                 CPLString osDim;
    1961           8 :                 switch (nSubRasterCount)
    1962             :                 {
    1963           0 :                     case 2:
    1964           0 :                         osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
    1965           0 :                         break;
    1966           8 :                     default:
    1967             :                         osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
    1968           8 :                                      nRasterYSize);
    1969           8 :                         break;
    1970             :                 }
    1971             : 
    1972           8 :                 const int nSubDataCount = 1 + m_aosSubdatasetMD.size() / 2;
    1973             :                 m_aosSubdatasetMD.SetNameValue(
    1974          16 :                     CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
    1975          24 :                     CPLString().Printf("%s", osPrettyName.c_str()));
    1976             : 
    1977             :                 m_aosSubdatasetMD.SetNameValue(
    1978          16 :                     CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
    1979           8 :                     CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
    1980             :                                        osPrettyName.c_str(),
    1981          16 :                                        GDALGetDataTypeName(eType)));
    1982             : 
    1983             :                 // add to PAM metadata
    1984           8 :                 if (!m_poSubDatasetsTree)
    1985             :                 {
    1986           3 :                     m_poSubDatasetsTree.reset(
    1987             :                         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
    1988             :                 }
    1989             : 
    1990           8 :                 CPLXMLNode *psSubNode = CPLCreateXMLNode(
    1991             :                     m_poSubDatasetsTree.get(), CXT_Element, "Subdataset");
    1992           8 :                 CPLAddXMLAttributeAndValue(psSubNode, "name",
    1993             :                                            osPrettyName.c_str());
    1994             : 
    1995           8 :                 CPLXMLNode *psMetaNode = CPLCreateXMLNode(
    1996             :                     CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
    1997             :                     CXT_Element, "Metadata");
    1998           8 :                 CPLAddXMLAttributeAndValue(psMetaNode, "domain",
    1999             :                                            GDAL_MDD_IMAGE_STRUCTURE);
    2000             : 
    2001           8 :                 CPLAddXMLAttributeAndValue(
    2002             :                     CPLCreateXMLElementAndValue(
    2003             :                         psMetaNode, "MDI",
    2004          16 :                         CPLString().Printf("%d", nRasterXSize)),
    2005             :                     "KEY", "X_SIZE");
    2006             : 
    2007           8 :                 CPLAddXMLAttributeAndValue(
    2008             :                     CPLCreateXMLElementAndValue(
    2009             :                         psMetaNode, "MDI",
    2010          16 :                         CPLString().Printf("%d", nRasterYSize)),
    2011             :                     "KEY", "Y_SIZE");
    2012             : 
    2013           8 :                 CPLAddXMLAttributeAndValue(
    2014             :                     CPLCreateXMLElementAndValue(
    2015             :                         psMetaNode, "MDI",
    2016          16 :                         CPLString().Printf("%s", GDALGetDataTypeName(eType))),
    2017             :                     "KEY", "DATA_TYPE");
    2018             : 
    2019           8 :                 if (m_lpoAttributeDS.size() > 0)
    2020             :                 {
    2021           6 :                     CPLAddXMLAttributeAndValue(
    2022             :                         CPLCreateXMLElementAndValue(
    2023             :                             psMetaNode, "MDI",
    2024          12 :                             CPLString().Printf("%d", nBands)),
    2025             :                         "KEY", "NUM_BANDS");
    2026             :                 }
    2027             :                 else
    2028             :                 {
    2029           2 :                     CPLAddXMLAttributeAndValue(
    2030             :                         CPLCreateXMLElementAndValue(
    2031             :                             psMetaNode, "MDI",
    2032           4 :                             CPLString().Printf("%d", nSubRasterCount)),
    2033             :                         "KEY", "NUM_BANDS");
    2034             :                 }
    2035             : 
    2036           8 :                 CPLAddXMLAttributeAndValue(
    2037             :                     CPLCreateXMLElementAndValue(
    2038             :                         psMetaNode, "MDI",
    2039          16 :                         CPLString().Printf("%d", nBitsPerSample)),
    2040             :                     "KEY", GDALMD_NBITS);
    2041             :             }
    2042             :         }
    2043         123 :         return CE_None;
    2044             :     }
    2045           0 :     catch (const tiledb::TileDBError &e)
    2046             :     {
    2047           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2048           0 :         return CE_Failure;
    2049             :     }
    2050             : }
    2051             : 
    2052             : /************************************************************************/
    2053             : /*                            SetBlockSize()                            */
    2054             : /************************************************************************/
    2055             : 
    2056           1 : void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
    2057             :                                        CPLStringList &aosOptions)
    2058             : 
    2059             : {
    2060           1 :     int nX = 0;
    2061           1 :     int nY = 0;
    2062           1 :     poBand->GetBlockSize(&nX, &nY);
    2063             : 
    2064           1 :     if (!aosOptions.FetchNameValue("BLOCKXSIZE"))
    2065             :     {
    2066           1 :         aosOptions.SetNameValue("BLOCKXSIZE", CPLString().Printf("%d", nX));
    2067             :     }
    2068             : 
    2069           1 :     if (!aosOptions.FetchNameValue("BLOCKYSIZE"))
    2070             :     {
    2071           1 :         aosOptions.SetNameValue("BLOCKYSIZE", CPLString().Printf("%d", nY));
    2072             :     }
    2073           1 : }
    2074             : 
    2075             : /************************************************************************/
    2076             : /*                              CreateLL()                              */
    2077             : /*                                                                      */
    2078             : /*      Shared functionality between TileDBDataset::Create() and        */
    2079             : /*      TileDBDataset::CreateCopy() for creating TileDB array based on  */
    2080             : /*      a set of options and a configuration.                           */
    2081             : /************************************************************************/
    2082             : 
    2083         120 : TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
    2084             :                                                    int nXSize, int nYSize,
    2085             :                                                    int nBandsIn,
    2086             :                                                    GDALDataType eType,
    2087             :                                                    CSLConstList papszOptions)
    2088             : {
    2089             :     try
    2090             :     {
    2091         120 :         if ((nXSize <= 0 && nYSize <= 0))
    2092             :         {
    2093           0 :             return nullptr;
    2094             :         }
    2095             : 
    2096         240 :         auto poDS = std::make_unique<TileDBRasterDataset>();
    2097         120 :         poDS->nRasterXSize = nXSize;
    2098         120 :         poDS->nRasterYSize = nYSize;
    2099         120 :         poDS->eDataType = eType;
    2100         120 :         poDS->nBands = nBandsIn;
    2101         120 :         poDS->eAccess = GA_Update;
    2102             : 
    2103         120 :         if (poDS->nBands == 0)  // subdatasets
    2104             :         {
    2105           1 :             poDS->eIndexMode = ATTRIBUTES;
    2106             :         }
    2107             :         else
    2108             :         {
    2109             :             const char *pszIndexMode =
    2110         119 :                 CSLFetchNameValue(papszOptions, GDALMD_INTERLEAVE);
    2111             : 
    2112         119 :             if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
    2113           0 :                 return nullptr;
    2114             :         }
    2115             : 
    2116             :         const char *pszConfig =
    2117         120 :             CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
    2118         120 :         if (pszConfig != nullptr)
    2119             :         {
    2120           0 :             poDS->m_osConfigFilename = pszConfig;
    2121           0 :             tiledb::Config cfg(pszConfig);
    2122           0 :             poDS->m_ctx.reset(new tiledb::Context(cfg));
    2123             :         }
    2124             :         else
    2125             :         {
    2126         120 :             poDS->m_ctx.reset(new tiledb::Context());
    2127             :         }
    2128             : 
    2129         120 :         if (CPLTestBool(
    2130             :                 CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "YES")))
    2131             :         {
    2132         110 :             poDS->m_bDatasetInGroup = true;
    2133         116 :             tiledb::create_group(*(poDS->m_ctx.get()), pszFilename);
    2134             : 
    2135             :             {
    2136         107 :                 tiledb::Group group(*(poDS->m_ctx.get()), pszFilename,
    2137         428 :                                     TILEDB_WRITE);
    2138         107 :                 group.put_metadata(
    2139             :                     DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    2140             :                     static_cast<int>(strlen(RASTER_DATASET_TYPE)),
    2141             :                     RASTER_DATASET_TYPE);
    2142             : 
    2143         107 :                 group.close();
    2144             :             }
    2145             : 
    2146             :             // Full resolution raster array
    2147         107 :             poDS->m_osArrayURI =
    2148         214 :                 CPLFormFilenameSafe(pszFilename, "l_0", nullptr);
    2149             :         }
    2150             :         else
    2151             :         {
    2152          10 :             poDS->m_osArrayURI = pszFilename;
    2153             :         }
    2154             : 
    2155             :         const char *pszCompression =
    2156         117 :             CSLFetchNameValue(papszOptions, GDALMD_COMPRESSION);
    2157             :         const char *pszCompressionLevel =
    2158         117 :             CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
    2159             : 
    2160             :         const char *pszBlockXSize =
    2161         117 :             CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
    2162         117 :         poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
    2163             :         const char *pszBlockYSize =
    2164         117 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
    2165         117 :         poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
    2166         117 :         poDS->bStats =
    2167         117 :             CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "STATS", FALSE));
    2168             : 
    2169             :         const char *pszTimestamp =
    2170         117 :             CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
    2171         117 :         if (pszTimestamp != nullptr)
    2172           2 :             poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
    2173             : 
    2174             :         // set dimensions and attribute type for schema
    2175         234 :         poDS->m_schema.reset(
    2176         117 :             new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
    2177         117 :         poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
    2178         117 :         poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
    2179             : 
    2180         117 :         poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
    2181             : 
    2182         117 :         if (pszCompression != nullptr)
    2183             :         {
    2184           0 :             int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
    2185           0 :             if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
    2186           0 :                                          *(poDS->m_filterList.get()),
    2187           0 :                                          pszCompression, nLevel) == CE_None)
    2188             :             {
    2189           0 :                 poDS->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
    2190           0 :                                       GDAL_MDD_IMAGE_STRUCTURE);
    2191           0 :                 poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
    2192             :             }
    2193             :         }
    2194             : 
    2195         234 :         CPLString osAux = CPLGetBasenameSafe(pszFilename);
    2196         117 :         osAux += ".tdb";
    2197             : 
    2198         234 :         poDS->SetPhysicalFilename(
    2199         234 :             CPLFormFilenameSafe(pszFilename, osAux.c_str(), nullptr).c_str());
    2200             : 
    2201             :         // Initialize PAM information.
    2202         117 :         poDS->SetDescription(pszFilename);
    2203             : 
    2204             :         // Note the dimension bounds are inclusive and are expanded to the match
    2205             :         // the block size
    2206         117 :         poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
    2207         117 :         poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
    2208             : 
    2209             :         // register additional attributes to the pixel value, these will be
    2210             :         // be reported as subdatasets on future reads
    2211             :         CSLConstList papszAttributes =
    2212         117 :             CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
    2213         121 :         for (const char *pszAttribute : cpl::Iterate(papszAttributes))
    2214             :         {
    2215             :             // modeling additional attributes as subdatasets
    2216           4 :             poDS->m_bHasSubDatasets = true;
    2217             :             // check each attribute is a GDAL source
    2218             :             std::unique_ptr<GDALDataset> poAttrDS(
    2219           8 :                 GDALDataset::Open(pszAttribute, GA_ReadOnly));
    2220             : 
    2221           4 :             if (poAttrDS != nullptr)
    2222             :             {
    2223             :                 // check each is co-registered
    2224             :                 // candidate band
    2225           4 :                 int nAttrBands = poAttrDS->GetRasterCount();
    2226           4 :                 if (nAttrBands > 0)
    2227             :                 {
    2228           4 :                     GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
    2229             : 
    2230           4 :                     if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
    2231           8 :                         (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
    2232           4 :                         (poDS->nBands == nAttrBands))
    2233             :                     {
    2234             :                         // could check geotransform, but it is sufficient
    2235             :                         // that cartesian dimensions are equal
    2236           4 :                         poDS->m_lpoAttributeDS.push_back(std::move(poAttrDS));
    2237             :                     }
    2238             :                     else
    2239             :                     {
    2240           0 :                         CPLError(
    2241             :                             CE_Warning, CPLE_AppDefined,
    2242             :                             "Skipping %s as it has a different dimension\n",
    2243             :                             pszAttribute);
    2244             :                     }
    2245             :                 }
    2246             :                 else
    2247             :                 {
    2248           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2249             :                              "Skipping %s as it doesn't have any bands\n",
    2250             :                              pszAttribute);
    2251             :                 }
    2252             :             }
    2253             :             else
    2254             :             {
    2255           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2256             :                          "Skipping %s, not recognized as a GDAL dataset\n",
    2257             :                          pszAttribute);
    2258             :             }
    2259             :         }
    2260             : 
    2261         117 :         return poDS.release();
    2262             :     }
    2263           3 :     catch (const tiledb::TileDBError &e)
    2264             :     {
    2265           3 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
    2266           3 :         return nullptr;
    2267             :     }
    2268             : }
    2269             : 
    2270             : /************************************************************************/
    2271             : /*                          DeferredCreate()                            */
    2272             : /*                                                                      */
    2273             : /*  Create dimension, domains and attributes. and optionally the array  */
    2274             : /************************************************************************/
    2275             : 
    2276         120 : bool TileDBRasterDataset::DeferredCreate(bool bCreateArray)
    2277             : {
    2278         120 :     CPLAssert(!m_bDeferredCreateHasRun);
    2279         120 :     m_bDeferredCreateHasRun = true;
    2280         120 :     m_bDeferredCreateHasBeenSuccessful = false;
    2281             : 
    2282             :     try
    2283             :     {
    2284             :         // this driver enforces that all subdatasets are the same size
    2285         240 :         tiledb::Domain domain(*m_ctx);
    2286             : 
    2287         120 :         const uint64_t w = static_cast<uint64_t>(nBlocksX) * nBlockXSize - 1;
    2288         120 :         const uint64_t h = static_cast<uint64_t>(nBlocksY) * nBlockYSize - 1;
    2289             : 
    2290         120 :         auto d1 = tiledb::Dimension::create<uint64_t>(*m_ctx, "X", {0, w},
    2291         483 :                                                       uint64_t(nBlockXSize));
    2292         117 :         auto d2 = tiledb::Dimension::create<uint64_t>(*m_ctx, "Y", {0, h},
    2293         468 :                                                       uint64_t(nBlockYSize));
    2294             : 
    2295             :         {
    2296             :             CPLErr eErr;
    2297             :             // Only used for unit test purposes (to check ability of GDAL to read
    2298             :             // an arbitrary array)
    2299             :             const char *pszAttrName =
    2300         117 :                 CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    2301         117 :             if ((nBands == 0) || (eIndexMode == ATTRIBUTES))
    2302             :             {
    2303           6 :                 eErr = AddDimensions(domain, pszAttrName, d2, d1, nullptr);
    2304             :             }
    2305             :             else
    2306             :             {
    2307             :                 auto d3 = tiledb::Dimension::create<uint64_t>(
    2308         222 :                     *m_ctx, "BANDS", {1, uint64_t(nBands)}, 1);
    2309         111 :                 eErr = AddDimensions(domain, pszAttrName, d2, d1, &d3);
    2310             :             }
    2311         117 :             if (eErr != CE_None)
    2312           0 :                 return false;
    2313             :         }
    2314             : 
    2315         117 :         m_schema->set_domain(domain).set_order(
    2316         117 :             {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
    2317             : 
    2318             :         // register additional attributes to the pixel value, these will be
    2319             :         // be reported as subdatasets on future reads
    2320         121 :         for (const auto &poAttrDS : m_lpoAttributeDS)
    2321             :         {
    2322             :             const std::string osAttrName =
    2323           4 :                 CPLGetBasenameSafe(poAttrDS->GetDescription());
    2324           4 :             GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
    2325           4 :             int bHasNoData = false;
    2326           4 :             const double dfNoData = poAttrBand->GetNoDataValue(&bHasNoData);
    2327           4 :             CreateAttribute(poAttrBand->GetRasterDataType(), osAttrName.c_str(),
    2328           4 :                             1, CPL_TO_BOOL(bHasNoData), dfNoData);
    2329             :         }
    2330             : 
    2331         117 :         if (bCreateArray)
    2332             :         {
    2333         116 :             CreateArray();
    2334             :         }
    2335             : 
    2336         117 :         m_bDeferredCreateHasBeenSuccessful = true;
    2337         117 :         return true;
    2338             :     }
    2339           3 :     catch (const tiledb::TileDBError &e)
    2340             :     {
    2341           3 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
    2342           3 :         return false;
    2343             :     }
    2344             : }
    2345             : 
    2346             : /************************************************************************/
    2347             : /*                            CreateArray()                             */
    2348             : /************************************************************************/
    2349             : 
    2350         117 : void TileDBRasterDataset::CreateArray()
    2351             : {
    2352         117 :     tiledb::Array::create(m_osArrayURI, *m_schema);
    2353             : 
    2354         117 :     if (m_bDatasetInGroup)
    2355             :     {
    2356         321 :         tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    2357         107 :         group.add_member(m_osArrayURI, false);
    2358         107 :         group.close();
    2359             :     }
    2360             : 
    2361         117 :     if (nTimestamp)
    2362           4 :         m_array.reset(new tiledb::Array(
    2363           2 :             *m_ctx, m_osArrayURI, TILEDB_WRITE,
    2364           6 :             tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
    2365             :     else
    2366         115 :         m_array.reset(new tiledb::Array(*m_ctx, m_osArrayURI, TILEDB_WRITE));
    2367         117 : }
    2368             : 
    2369             : /************************************************************************/
    2370             : /*                              CopySubDatasets()                       */
    2371             : /*                                                                      */
    2372             : /*      Copy SubDatasets from src to a TileDBRasterDataset              */
    2373             : /*                                                                      */
    2374             : /************************************************************************/
    2375             : 
    2376           1 : CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
    2377             :                                             TileDBRasterDataset *poDstDS,
    2378             :                                             GDALProgressFunc pfnProgress,
    2379             :                                             void *pProgressData)
    2380             : 
    2381             : {
    2382             :     try
    2383             :     {
    2384           2 :         std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
    2385           1 :         poDstDS->m_bHasSubDatasets = true;
    2386             :         CSLConstList papszSrcSubDatasets =
    2387           1 :             poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
    2388           1 :         if (!papszSrcSubDatasets)
    2389           0 :             return CE_Failure;
    2390             :         const char *pszSubDSName =
    2391           1 :             CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    2392           1 :         if (!pszSubDSName)
    2393           0 :             return CE_Failure;
    2394             : 
    2395             :         CPLStringList apszTokens(CSLTokenizeString2(
    2396           2 :             pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
    2397             :         // FIXME? this is tailored for HDF5-like subdataset names
    2398             :         // HDF5:foo.hdf5:attrname.
    2399           1 :         if (apszTokens.size() != 3)
    2400             :         {
    2401           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2402             :                      "Cannot guess attribute name in %s", pszSubDSName);
    2403           0 :             return CE_Failure;
    2404             :         }
    2405             : 
    2406             :         std::unique_ptr<GDALDataset> poSubDataset(
    2407           2 :             GDALDataset::Open(pszSubDSName));
    2408           2 :         if (poSubDataset.get() == nullptr ||
    2409           1 :             poSubDataset->GetRasterCount() == 0)
    2410             :         {
    2411           0 :             return CE_Failure;
    2412             :         }
    2413             : 
    2414           1 :         uint64_t nSubXSize = poSubDataset->GetRasterXSize();
    2415           1 :         uint64_t nSubYSize = poSubDataset->GetRasterYSize();
    2416             : 
    2417           1 :         const char *pszAttrName = apszTokens[2];
    2418             : 
    2419           1 :         auto poFirstSubDSBand = poSubDataset->GetRasterBand(1);
    2420           1 :         int bFirstSubDSBandHasNoData = FALSE;
    2421             :         const double dfFirstSubDSBandNoData =
    2422           1 :             poFirstSubDSBand->GetNoDataValue(&bFirstSubDSBandHasNoData);
    2423           1 :         poDstDS->CreateAttribute(poFirstSubDSBand->GetRasterDataType(),
    2424             :                                  pszAttrName, poSubDataset->GetRasterCount(),
    2425           1 :                                  CPL_TO_BOOL(bFirstSubDSBandHasNoData),
    2426             :                                  dfFirstSubDSBandNoData);
    2427           1 :         apoDatasets.push_back(std::move(poSubDataset));
    2428             : 
    2429           8 :         for (const auto &[pszKey, pszValue] :
    2430           9 :              cpl::IterateNameValue(papszSrcSubDatasets))
    2431             :         {
    2432           4 :             if (EQUAL(pszKey, "SUBDATASET_1_NAME") || !strstr(pszKey, "_NAME"))
    2433             :             {
    2434           3 :                 continue;
    2435             :             }
    2436           1 :             pszSubDSName = pszValue;
    2437             :             apszTokens = CSLTokenizeString2(
    2438           1 :                 pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
    2439           1 :             if (apszTokens.size() != 3)
    2440             :             {
    2441           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2442             :                          "Cannot guess attribute name in %s", pszSubDSName);
    2443           0 :                 continue;
    2444             :             }
    2445             : 
    2446             :             std::unique_ptr<GDALDataset> poSubDS(
    2447           2 :                 GDALDataset::Open(pszSubDSName));
    2448           1 :             if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
    2449             :             {
    2450           1 :                 GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
    2451             :                 int nBlockXSize, nBlockYSize;
    2452           1 :                 poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2453             : 
    2454           1 :                 int bHasNoData = FALSE;
    2455           1 :                 const double dfNoData = poBand->GetNoDataValue(&bHasNoData);
    2456             : 
    2457           1 :                 if ((poSubDS->GetRasterXSize() !=
    2458           1 :                      static_cast<int>(nSubXSize)) ||
    2459           1 :                     (poSubDS->GetRasterYSize() !=
    2460           1 :                      static_cast<int>(nSubYSize)) ||
    2461           3 :                     (nBlockXSize != poDstDS->nBlockXSize) ||
    2462           1 :                     (nBlockYSize != poDstDS->nBlockYSize))
    2463             :                 {
    2464           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2465             :                              "Sub-datasets must have the same dimension,"
    2466             :                              " and block sizes, skipping %s",
    2467             :                              pszSubDSName);
    2468             :                 }
    2469             :                 else
    2470             :                 {
    2471           1 :                     pszAttrName = apszTokens[2];
    2472           1 :                     poDstDS->CreateAttribute(
    2473             :                         poSubDS->GetRasterBand(1)->GetRasterDataType(),
    2474             :                         pszAttrName, poSubDS->GetRasterCount(),
    2475           1 :                         CPL_TO_BOOL(bHasNoData), dfNoData);
    2476           1 :                     apoDatasets.push_back(std::move(poSubDS));
    2477             :                 }
    2478             :             }
    2479             :             else
    2480             :             {
    2481           0 :                 CPLError(
    2482             :                     CE_Warning, CPLE_AppDefined,
    2483             :                     "Sub-datasets must be not null and contain data in bands,"
    2484             :                     "skipping %s\n",
    2485             :                     pszSubDSName);
    2486             :             }
    2487             :         }
    2488             : 
    2489           1 :         poDstDS->SetMetadata(poDstDS->m_aosSubdatasetMD.List(),
    2490           1 :                              GDAL_MDD_SUBDATASETS);
    2491             : 
    2492           1 :         poDstDS->CreateArray();
    2493             : 
    2494             :         /* --------------------------------------------------------  */
    2495             :         /*      Report preliminary (0) progress.                     */
    2496             :         /* --------------------------------------------------------- */
    2497           1 :         if (!pfnProgress(0.0, nullptr, pProgressData))
    2498             :         {
    2499           0 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    2500             :                      "User terminated CreateCopy()");
    2501           0 :             return CE_Failure;
    2502             :         }
    2503             : 
    2504             :         // copy over subdatasets by block
    2505           2 :         tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
    2506           1 :         query.set_layout(TILEDB_GLOBAL_ORDER);
    2507           1 :         const uint64_t nTotalBlocks =
    2508           1 :             static_cast<uint64_t>(poDstDS->nBlocksX) * poDstDS->nBlocksY;
    2509           1 :         uint64_t nBlockCounter = 0;
    2510             : 
    2511             :         // row-major
    2512           6 :         for (int j = 0; j < poDstDS->nBlocksY; ++j)
    2513             :         {
    2514          55 :             for (int i = 0; i < poDstDS->nBlocksX; ++i)
    2515             :             {
    2516          50 :                 std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
    2517             :                 // have to write set all tiledb attributes on write
    2518          50 :                 int iAttr = 0;
    2519         150 :                 for (auto &poSubDS : apoDatasets)
    2520             :                 {
    2521             :                     const GDALDataType eDT =
    2522         100 :                         poSubDS->GetRasterBand(1)->GetRasterDataType();
    2523             : 
    2524         200 :                     for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
    2525             :                     {
    2526         100 :                         const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
    2527         100 :                         const size_t nValues =
    2528         100 :                             static_cast<size_t>(poDstDS->nBlockXSize) *
    2529         100 :                             poDstDS->nBlockYSize;
    2530         100 :                         void *pBlock = VSI_MALLOC_VERBOSE(nDTSize * nValues);
    2531         100 :                         if (!pBlock)
    2532           0 :                             return CE_Failure;
    2533         100 :                         aBlocks.emplace_back(pBlock, &VSIFree);
    2534         100 :                         GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
    2535         100 :                         if (poBand->ReadBlock(i, j, pBlock) == CE_None)
    2536             :                         {
    2537         100 :                             SetBuffer(
    2538             :                                 &query, eDT,
    2539         200 :                                 poDstDS->m_schema->attribute(iAttr).name(),
    2540             :                                 pBlock, nValues);
    2541             :                         }
    2542         100 :                         ++iAttr;
    2543             :                     }
    2544             :                 }
    2545             : 
    2546          50 :                 if (poDstDS->bStats)
    2547           0 :                     tiledb::Stats::enable();
    2548             : 
    2549          50 :                 auto status = query.submit();
    2550             : 
    2551          50 :                 if (poDstDS->bStats)
    2552             :                 {
    2553           0 :                     tiledb::Stats::dump(stdout);
    2554           0 :                     tiledb::Stats::disable();
    2555             :                 }
    2556             : 
    2557          50 :                 if (status == tiledb::Query::Status::FAILED)
    2558             :                 {
    2559           0 :                     return CE_Failure;
    2560             :                 }
    2561             : 
    2562          50 :                 ++nBlockCounter;
    2563          50 :                 if (!pfnProgress(static_cast<double>(nBlockCounter) /
    2564          50 :                                      static_cast<double>(nTotalBlocks),
    2565             :                                  nullptr, pProgressData))
    2566             :                 {
    2567           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt,
    2568             :                              "User terminated CreateCopy()");
    2569           0 :                     return CE_Failure;
    2570             :                 }
    2571             :             }
    2572             :         }
    2573             : 
    2574           1 :         query.finalize();
    2575             : 
    2576           1 :         return CE_None;
    2577             :     }
    2578           0 :     catch (const tiledb::TileDBError &e)
    2579             :     {
    2580           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2581           0 :         return CE_Failure;
    2582             :     }
    2583             : }
    2584             : 
    2585             : /************************************************************************/
    2586             : /*                               Create()                               */
    2587             : /************************************************************************/
    2588             : 
    2589         119 : TileDBRasterDataset *TileDBRasterDataset::Create(const char *pszFilename,
    2590             :                                                  int nXSize, int nYSize,
    2591             :                                                  int nBandsIn,
    2592             :                                                  GDALDataType eType,
    2593             :                                                  CSLConstList papszOptions)
    2594             : 
    2595             : {
    2596         238 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2597             : 
    2598             :     std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
    2599         238 :         osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
    2600             : 
    2601         119 :     if (!poDS)
    2602           3 :         return nullptr;
    2603             : 
    2604             :     const char *pszAttrName =
    2605         116 :         CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    2606         314 :     for (int i = 0; i < poDS->nBands; i++)
    2607             :     {
    2608         198 :         if (poDS->eIndexMode == ATTRIBUTES)
    2609          24 :             poDS->SetBand(
    2610             :                 i + 1, new TileDBRasterBand(
    2611          12 :                            poDS.get(), i + 1,
    2612          24 :                            TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
    2613             :         else
    2614         558 :             poDS->SetBand(i + 1,
    2615         372 :                           new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
    2616             :     }
    2617             : 
    2618             :     // TILEDB_WRITE_IMAGE_STRUCTURE=NO only used for unit test purposes (to
    2619             :     // check ability of GDAL to read an arbitrary array)
    2620         116 :     if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
    2621             :     {
    2622         204 :         CPLStringList aosImageStruct;
    2623             :         aosImageStruct.SetNameValue(
    2624         102 :             GDALMD_NBITS, CPLString().Printf("%d", poDS->nBitsPerSample));
    2625             :         aosImageStruct.SetNameValue(
    2626             :             "DATA_TYPE",
    2627         102 :             CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
    2628             :         aosImageStruct.SetNameValue(
    2629         102 :             "X_SIZE", CPLString().Printf("%d", poDS->nRasterXSize));
    2630             :         aosImageStruct.SetNameValue(
    2631         102 :             "Y_SIZE", CPLString().Printf("%d", poDS->nRasterYSize));
    2632             :         aosImageStruct.SetNameValue(GDALMD_INTERLEAVE,
    2633         102 :                                     index_type_name(poDS->eIndexMode));
    2634         102 :         aosImageStruct.SetNameValue("DATASET_TYPE", RASTER_DATASET_TYPE);
    2635             : 
    2636         102 :         if (poDS->m_lpoAttributeDS.size() > 0)
    2637             :         {
    2638           2 :             int i = 0;
    2639           6 :             for (auto const &poAttrDS : poDS->m_lpoAttributeDS)
    2640             :             {
    2641           4 :                 aosImageStruct.SetNameValue(
    2642           8 :                     CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
    2643          12 :                     CPLGetBasenameSafe(poAttrDS->GetDescription()).c_str());
    2644             :             }
    2645             :         }
    2646         102 :         poDS->SetMetadata(aosImageStruct.List(), GDAL_MDD_IMAGE_STRUCTURE);
    2647             :     }
    2648             : 
    2649         116 :     return poDS.release();
    2650             : }
    2651             : 
    2652             : /************************************************************************/
    2653             : /*                             CreateCopy()                             */
    2654             : /************************************************************************/
    2655             : 
    2656          52 : GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
    2657             :                                              GDALDataset *poSrcDS, int bStrict,
    2658             :                                              CSLConstList papszOptions,
    2659             :                                              GDALProgressFunc pfnProgress,
    2660             :                                              void *pProgressData)
    2661             : 
    2662             : {
    2663         104 :     CPLStringList aosOptions(CSLDuplicate(papszOptions));
    2664         104 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2665             : 
    2666          52 :     std::unique_ptr<TileDBRasterDataset> poDstDS;
    2667             : 
    2668          52 :     if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
    2669             :     {
    2670             :         // TileDB schemas are fixed
    2671           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2672             :                  "TileDB driver does not support "
    2673             :                  "appending to an existing schema.");
    2674           0 :         return nullptr;
    2675             :     }
    2676             : 
    2677             :     CSLConstList papszSrcSubDatasets =
    2678          52 :         poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
    2679             : 
    2680          52 :     if (papszSrcSubDatasets == nullptr)
    2681             :     {
    2682          51 :         const int nBands = poSrcDS->GetRasterCount();
    2683             : 
    2684          51 :         if (nBands > 0)
    2685             :         {
    2686          51 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
    2687          51 :             GDALDataType eType = poBand->GetRasterDataType();
    2688             : 
    2689          85 :             for (int i = 2; i <= nBands; ++i)
    2690             :             {
    2691          34 :                 if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
    2692             :                 {
    2693           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2694             :                              "TileDB driver does not support "
    2695             :                              "source dataset with different band data types.");
    2696           0 :                     return nullptr;
    2697             :                 }
    2698             :             }
    2699             : 
    2700          51 :             poDstDS.reset(TileDBRasterDataset::Create(
    2701             :                 osArrayPath, poSrcDS->GetRasterXSize(),
    2702             :                 poSrcDS->GetRasterYSize(), nBands, eType, papszOptions));
    2703             : 
    2704          51 :             if (!poDstDS)
    2705             :             {
    2706           3 :                 return nullptr;
    2707             :             }
    2708             : 
    2709         130 :             for (int i = 1; i <= nBands; ++i)
    2710             :             {
    2711          82 :                 int bHasNoData = FALSE;
    2712             :                 const double dfNoData =
    2713          82 :                     poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
    2714          82 :                 if (bHasNoData)
    2715           3 :                     poDstDS->GetRasterBand(i)->SetNoDataValue(dfNoData);
    2716             :             }
    2717             : 
    2718             :             CPLErr eErr =
    2719          48 :                 GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
    2720             :                                            pfnProgress, pProgressData);
    2721             : 
    2722          48 :             if (eErr != CE_None)
    2723             :             {
    2724           0 :                 CPLError(eErr, CPLE_AppDefined,
    2725             :                          "Error copying raster to TileDB.");
    2726           0 :                 return nullptr;
    2727             :             }
    2728             :         }
    2729             :         else
    2730             :         {
    2731           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2732             :                      "TileDB driver does not support "
    2733             :                      "source datasets with zero bands.");
    2734           0 :             return nullptr;
    2735             :         }
    2736             :     }
    2737             :     else
    2738             :     {
    2739           1 :         if (bStrict)
    2740             :         {
    2741           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2742             :                      "TileDB driver does not support copying "
    2743             :                      "subdatasets in strict mode.");
    2744           0 :             return nullptr;
    2745             :         }
    2746             : 
    2747           2 :         if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
    2748           1 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
    2749             :         {
    2750           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2751             :                      "Changing block size is not supported when copying "
    2752             :                      "subdatasets.");
    2753           0 :             return nullptr;
    2754             :         }
    2755             : 
    2756           1 :         const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
    2757             :         const int nMaxFiles =
    2758           1 :             atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
    2759             : 
    2760           1 :         aosOptions.SetNameValue("CREATE_GROUP", "NO");
    2761             : 
    2762           1 :         if (nSubDatasetCount <= nMaxFiles)
    2763             :         {
    2764             :             const char *pszSource =
    2765           1 :                 CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    2766           1 :             if (pszSource)
    2767             :             {
    2768             :                 std::unique_ptr<GDALDataset> poSubDataset(
    2769           1 :                     GDALDataset::Open(pszSource));
    2770           1 :                 if (poSubDataset && poSubDataset->GetRasterCount() > 0)
    2771             :                 {
    2772           1 :                     GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
    2773             : 
    2774           1 :                     TileDBRasterDataset::SetBlockSize(poBand, aosOptions);
    2775           1 :                     poDstDS.reset(TileDBRasterDataset::CreateLL(
    2776             :                         osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
    2777           1 :                         poBand->GetRasterDataType(), aosOptions.List()));
    2778             : 
    2779           1 :                     if (poDstDS)
    2780             :                     {
    2781           1 :                         if (!poDstDS->DeferredCreate(
    2782             :                                 /* bCreateArray = */ false))
    2783           0 :                             return nullptr;
    2784             : 
    2785           1 :                         if (TileDBRasterDataset::CopySubDatasets(
    2786             :                                 poSrcDS, poDstDS.get(), pfnProgress,
    2787           1 :                                 pProgressData) != CE_None)
    2788             :                         {
    2789           0 :                             poDstDS.reset();
    2790           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    2791             :                                      "Unable to copy subdatasets.");
    2792             :                         }
    2793             :                     }
    2794             :                 }
    2795             :             }
    2796             :         }
    2797             :         else
    2798             :         {
    2799           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2800             :                      "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
    2801             :         }
    2802             :     }
    2803             : 
    2804             :     // TODO Supporting mask bands is a possible future task
    2805          49 :     if (poDstDS != nullptr)
    2806             :     {
    2807          49 :         int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    2808          49 :         poDstDS->CloneInfo(poSrcDS, nCloneFlags);
    2809             : 
    2810          49 :         if (poDstDS->FlushCache(false) != CE_None)
    2811             :         {
    2812           0 :             CPLError(CE_Failure, CPLE_AppDefined, "FlushCache() failed");
    2813           0 :             return nullptr;
    2814             :         }
    2815             : 
    2816          49 :         return poDstDS.release();
    2817             :     }
    2818           0 :     return nullptr;
    2819             : }
    2820             : 
    2821             : /************************************************************************/
    2822             : /*                           LoadOverviews()                            */
    2823             : /************************************************************************/
    2824             : 
    2825          28 : void TileDBRasterDataset::LoadOverviews()
    2826             : {
    2827          28 :     if (m_bLoadOverviewsDone)
    2828          23 :         return;
    2829           5 :     m_bLoadOverviewsDone = true;
    2830             : 
    2831             :     // NOTE: read overview_model.rst for a high level explanation of overviews
    2832             :     // are stored.
    2833             : 
    2834           5 :     if (!m_bDatasetInGroup)
    2835           0 :         return;
    2836             : 
    2837           5 :     CPLStringList aosOpenOptions;
    2838           5 :     if (nTimestamp)
    2839             :     {
    2840             :         aosOpenOptions.SetNameValue("TILEDB_TIMESTAMP",
    2841           0 :                                     CPLSPrintf("%" PRIu64, nTimestamp));
    2842             :     }
    2843           5 :     if (!m_osConfigFilename.empty())
    2844             :     {
    2845             :         aosOpenOptions.SetNameValue("TILEDB_CONFIG",
    2846           0 :                                     m_osConfigFilename.c_str());
    2847             :     }
    2848          10 :     for (int i = 0; i < m_nOverviewCountFromMetadata; ++i)
    2849             :     {
    2850           5 :         const std::string osArrayName = CPLSPrintf("l_%d", 1 + i);
    2851             :         const std::string osOvrDatasetName =
    2852           5 :             CPLFormFilenameSafe(GetDescription(), osArrayName.c_str(), nullptr);
    2853             : 
    2854           5 :         GDALOpenInfo oOpenInfo(osOvrDatasetName.c_str(), eAccess);
    2855           5 :         oOpenInfo.papszOpenOptions = aosOpenOptions.List();
    2856             :         auto poOvrDS = std::unique_ptr<GDALDataset>(
    2857           5 :             Open(&oOpenInfo, tiledb::Object::Type::Array));
    2858           5 :         if (!poOvrDS)
    2859           0 :             return;
    2860           5 :         if (poOvrDS->GetRasterCount() != nBands)
    2861             :         {
    2862           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2863             :                      "Overview %s has not the same number of bands as full "
    2864             :                      "resolution dataset",
    2865             :                      osOvrDatasetName.c_str());
    2866           0 :             return;
    2867             :         }
    2868           5 :         m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    2869             :     }
    2870             : }
    2871             : 
    2872             : /************************************************************************/
    2873             : /*                          IBuildOverviews()                           */
    2874             : /************************************************************************/
    2875             : 
    2876          13 : CPLErr TileDBRasterDataset::IBuildOverviews(
    2877             :     const char *pszResampling, int nOverviews, const int *panOverviewList,
    2878             :     int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
    2879             :     void *pProgressData, CSLConstList papszOptions)
    2880             : {
    2881             :     // NOTE: read overview_model.rst for a high level explanation of overviews
    2882             :     // are stored.
    2883             : 
    2884          13 :     if (eAccess == GA_ReadOnly)
    2885             :     {
    2886           3 :         if (!CPLTestBool(
    2887             :                 CPLGetConfigOption("TILEDB_GEOTIFF_OVERVIEWS", "FALSE")))
    2888             :         {
    2889           2 :             ReportError(
    2890             :                 CE_Failure, CPLE_NotSupported,
    2891             :                 "Cannot %s overviews in TileDB format in read-only mode",
    2892             :                 nOverviews ? "create" : "delete");
    2893           2 :             return CE_Failure;
    2894             :         }
    2895             : 
    2896             :         // GeoTIFF overviews. This used to be supported before GDAL 3.10
    2897             :         // although likely not desirable.
    2898           1 :         return GDALPamDataset::IBuildOverviews(
    2899             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    2900           1 :             pfnProgress, pProgressData, papszOptions);
    2901             :     }
    2902             : 
    2903          10 :     if (nBands == 0)
    2904             :     {
    2905           0 :         return CE_Failure;
    2906             :     }
    2907             : 
    2908             :     // If we already have PAM overview (i.e. GeoTIFF based), go through PAM
    2909          10 :     if (cpl::down_cast<GDALPamRasterBand *>(GetRasterBand(1))
    2910          10 :             ->GDALPamRasterBand::GetOverviewCount() > 0)
    2911             :     {
    2912           1 :         return GDALPamDataset::IBuildOverviews(
    2913             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    2914           1 :             pfnProgress, pProgressData, papszOptions);
    2915             :     }
    2916             : 
    2917           9 :     if (!m_bDatasetInGroup)
    2918             :     {
    2919           2 :         ReportError(CE_Failure, CPLE_NotSupported,
    2920             :                     "IBuildOverviews() only supported for datasets created "
    2921             :                     "with CREATE_GROUP=YES");
    2922           2 :         return CE_Failure;
    2923             :     }
    2924             : 
    2925             :     /* -------------------------------------------------------------------- */
    2926             :     /*      Our overview support currently only works safely if all         */
    2927             :     /*      bands are handled at the same time.                             */
    2928             :     /* -------------------------------------------------------------------- */
    2929           7 :     if (nListBands != nBands)
    2930             :     {
    2931           0 :         ReportError(CE_Failure, CPLE_NotSupported,
    2932             :                     "Generation of TileDB overviews currently only "
    2933             :                     "supported when operating on all bands.  "
    2934             :                     "Operation failed.");
    2935           0 :         return CE_Failure;
    2936             :     }
    2937             : 
    2938             :     // Force loading existing overviews
    2939           7 :     if (m_nOverviewCountFromMetadata)
    2940           4 :         GetRasterBand(1)->GetOverviewCount();
    2941           7 :     m_bLoadOverviewsDone = true;
    2942             : 
    2943             :     /* -------------------------------------------------------------------- */
    2944             :     /*      Deletes existing overviews if requested.                        */
    2945             :     /* -------------------------------------------------------------------- */
    2946           7 :     if (nOverviews == 0)
    2947             :     {
    2948           2 :         CPLErr eErr = CE_None;
    2949             : 
    2950             :         // Unlink arrays from he group
    2951             :         try
    2952             :         {
    2953           6 :             tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    2954           4 :             for (auto &&poODS : m_apoOverviewDS)
    2955             :             {
    2956           2 :                 group.remove_member(poODS->GetDescription());
    2957             :             }
    2958           2 :             group.close();
    2959             :         }
    2960           0 :         catch (const tiledb::TileDBError &e)
    2961             :         {
    2962           0 :             eErr = CE_Failure;
    2963           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2964             :         }
    2965             : 
    2966           2 :         tiledb::VFS vfs(*m_ctx, m_ctx->config());
    2967             : 
    2968             :         // Delete arrays
    2969           4 :         for (auto &&poODS : m_apoOverviewDS)
    2970             :         {
    2971             :             try
    2972             :             {
    2973           2 :                 CPL_IGNORE_RET_VAL(poODS->Close());
    2974           2 :                 tiledb::Array::delete_array(*m_ctx, poODS->GetDescription());
    2975           2 :                 if (vfs.is_dir(poODS->GetDescription()))
    2976             :                 {
    2977           2 :                     vfs.remove_dir(poODS->GetDescription());
    2978             :                 }
    2979             :             }
    2980           0 :             catch (const tiledb::TileDBError &e)
    2981             :             {
    2982           0 :                 eErr = CE_Failure;
    2983           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2984             :                          "Array::delete_array(%s) failed: %s",
    2985           0 :                          poODS->GetDescription(), e.what());
    2986             :             }
    2987           2 :             m_apoOverviewDSRemoved.emplace_back(std::move(poODS));
    2988             :         }
    2989             : 
    2990           2 :         m_apoOverviewDS.clear();
    2991           2 :         m_nOverviewCountFromMetadata = 0;
    2992           2 :         MarkPamDirty();
    2993           2 :         return eErr;
    2994             :     }
    2995             : 
    2996             :     /* -------------------------------------------------------------------- */
    2997             :     /*      Establish which of the overview levels we already have, and     */
    2998             :     /*      which are new.                                                  */
    2999             :     /* -------------------------------------------------------------------- */
    3000          10 :     std::vector<bool> abRequireNewOverview(nOverviews, true);
    3001          10 :     for (int i = 0; i < nOverviews; ++i)
    3002             :     {
    3003           5 :         const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
    3004           5 :         const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
    3005             : 
    3006           6 :         for (const auto &poODS : m_apoOverviewDS)
    3007             :         {
    3008             :             const int nOvFactor =
    3009           2 :                 GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
    3010             :                                     poODS->GetRasterYSize(), GetRasterYSize());
    3011             : 
    3012             :             // If we already have a 1x1 overview and this new one would result
    3013             :             // in it too, then don't create it.
    3014           2 :             if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
    3015           2 :                 nOXSize == 1 && nOYSize == 1)
    3016             :             {
    3017           0 :                 abRequireNewOverview[i] = false;
    3018           0 :                 break;
    3019             :             }
    3020             : 
    3021           3 :             if (nOvFactor == panOverviewList[i] ||
    3022           1 :                 nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3023             :                                                 GetRasterXSize(),
    3024             :                                                 GetRasterYSize()))
    3025             :             {
    3026           1 :                 abRequireNewOverview[i] = false;
    3027           1 :                 break;
    3028             :             }
    3029             :         }
    3030             : 
    3031           5 :         if (abRequireNewOverview[i])
    3032             :         {
    3033           4 :             CPLStringList aosCreationOptions;
    3034           4 :             aosCreationOptions.SetNameValue("CREATE_GROUP", "NO");
    3035             :             aosCreationOptions.SetNameValue(
    3036           4 :                 GDALMD_NBITS, CPLString().Printf("%d", nBitsPerSample));
    3037             :             aosCreationOptions.SetNameValue(GDALMD_INTERLEAVE,
    3038           4 :                                             index_type_name(eIndexMode));
    3039           4 :             if (nTimestamp)
    3040             :             {
    3041             :                 aosCreationOptions.SetNameValue(
    3042           0 :                     "TILEDB_TIMESTAMP", CPLSPrintf("%" PRIu64, nTimestamp));
    3043             :             }
    3044           4 :             if (!m_osConfigFilename.empty())
    3045             :             {
    3046             :                 aosCreationOptions.SetNameValue("TILEDB_CONFIG",
    3047           0 :                                                 m_osConfigFilename.c_str());
    3048             :             }
    3049             : 
    3050             :             const std::string osArrayName =
    3051           4 :                 CPLSPrintf("l_%d", 1 + int(m_apoOverviewDS.size()));
    3052             :             const std::string osOvrDatasetName = CPLFormFilenameSafe(
    3053           4 :                 GetDescription(), osArrayName.c_str(), nullptr);
    3054             : 
    3055             :             auto poOvrDS = std::unique_ptr<TileDBRasterDataset>(
    3056             :                 Create(osOvrDatasetName.c_str(), nOXSize, nOYSize, nBands,
    3057             :                        GetRasterBand(1)->GetRasterDataType(),
    3058           4 :                        aosCreationOptions.List()));
    3059           4 :             if (!poOvrDS)
    3060           0 :                 return CE_Failure;
    3061             : 
    3062             :             // Apply nodata from main dataset
    3063          16 :             for (int j = 0; j < nBands; ++j)
    3064             :             {
    3065          12 :                 int bHasNoData = FALSE;
    3066             :                 const double dfNoData =
    3067          12 :                     GetRasterBand(j + 1)->GetNoDataValue(&bHasNoData);
    3068          12 :                 if (bHasNoData)
    3069          12 :                     poOvrDS->GetRasterBand(j + 1)->SetNoDataValue(dfNoData);
    3070             :             }
    3071             : 
    3072             :             // Apply georeferencing from main dataset
    3073           4 :             poOvrDS->SetSpatialRef(GetSpatialRef());
    3074           4 :             GDALGeoTransform gt;
    3075           4 :             if (GetGeoTransform(gt) == CE_None)
    3076             :             {
    3077           4 :                 gt.Rescale(static_cast<double>(nRasterXSize) / nOXSize,
    3078           4 :                            static_cast<double>(nRasterYSize) / nOYSize);
    3079           4 :                 poOvrDS->SetGeoTransform(gt);
    3080             :             }
    3081             : 
    3082           4 :             poOvrDS->DeferredCreate(/* bCreateArray = */ true);
    3083             : 
    3084             :             try
    3085             :             {
    3086          12 :                 tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
    3087           4 :                 group.add_member(osOvrDatasetName, false);
    3088           4 :                 group.close();
    3089             :             }
    3090           0 :             catch (const tiledb::TileDBError &e)
    3091             :             {
    3092           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    3093             :             }
    3094             : 
    3095           4 :             m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    3096             :         }
    3097             :     }
    3098             : 
    3099           5 :     m_nOverviewCountFromMetadata = static_cast<int>(m_apoOverviewDS.size());
    3100           5 :     MarkPamDirty();
    3101             : 
    3102             :     /* -------------------------------------------------------------------- */
    3103             :     /*      Refresh/generate overviews that are listed.                     */
    3104             :     /* -------------------------------------------------------------------- */
    3105          10 :     std::vector<GDALRasterBand *> apoSrcBands;
    3106          10 :     std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands;
    3107           5 :     CPLErr eErr = CE_None;
    3108             :     const auto osNormalizedResampling =
    3109           5 :         GDALGetNormalizedOvrResampling(pszResampling);
    3110          20 :     for (int iBand = 0; eErr == CE_None && iBand < nBands; iBand++)
    3111             :     {
    3112          15 :         apoSrcBands.push_back(GetRasterBand(iBand + 1));
    3113          30 :         std::vector<GDALRasterBand *> apoOverviewBands;
    3114             : 
    3115             :         std::vector<bool> abAlreadyUsedOverviewBand(m_apoOverviewDS.size(),
    3116          30 :                                                     false);
    3117             : 
    3118          30 :         for (int i = 0; i < nOverviews; i++)
    3119             :         {
    3120          15 :             bool bFound = false;
    3121          18 :             for (size_t j = 0; j < m_apoOverviewDS.size(); ++j)
    3122             :             {
    3123          18 :                 if (!abAlreadyUsedOverviewBand[j])
    3124             :                 {
    3125          18 :                     auto &poODS = m_apoOverviewDS[j];
    3126          18 :                     int nOvFactor = GDALComputeOvFactor(
    3127             :                         poODS->GetRasterXSize(), nRasterXSize,
    3128             :                         poODS->GetRasterYSize(), nRasterYSize);
    3129             : 
    3130          21 :                     if (nOvFactor == panOverviewList[i] ||
    3131           3 :                         nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    3132             :                                                         nRasterXSize,
    3133             :                                                         nRasterYSize))
    3134             :                     {
    3135          15 :                         abAlreadyUsedOverviewBand[j] = true;
    3136          15 :                         auto poOvrBand = poODS->GetRasterBand(iBand + 1);
    3137          15 :                         if (!osNormalizedResampling.empty())
    3138             :                         {
    3139             :                             // Store resampling method in band metadata, as it
    3140             :                             // can be used by the gdaladdo utilities to refresh
    3141             :                             // existing overviews with the method previously
    3142             :                             // used
    3143          15 :                             poOvrBand->SetMetadataItem(
    3144          15 :                                 "RESAMPLING", osNormalizedResampling.c_str());
    3145             :                         }
    3146          15 :                         apoOverviewBands.push_back(poOvrBand);
    3147          15 :                         bFound = true;
    3148          15 :                         break;
    3149             :                     }
    3150             :                 }
    3151             :             }
    3152          15 :             if (!bFound)
    3153             :             {
    3154           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3155             :                          "Could not find dataset corresponding to ov factor %d",
    3156           0 :                          panOverviewList[i]);
    3157           0 :                 eErr = CE_Failure;
    3158             :             }
    3159             :         }
    3160          15 :         if (iBand > 0)
    3161             :         {
    3162          10 :             CPLAssert(apoOverviewBands.size() == aapoOverviewBands[0].size());
    3163             :         }
    3164          15 :         aapoOverviewBands.emplace_back(std::move(apoOverviewBands));
    3165             :     }
    3166             : 
    3167           5 :     if (eErr == CE_None)
    3168             :     {
    3169           5 :         eErr = GDALRegenerateOverviewsMultiBand(apoSrcBands, aapoOverviewBands,
    3170             :                                                 pszResampling, pfnProgress,
    3171             :                                                 pProgressData, papszOptions);
    3172             :     }
    3173             : 
    3174           5 :     return eErr;
    3175             : }

Generated by: LCOV version 1.14