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

Generated by: LCOV version 1.14