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

Generated by: LCOV version 1.14