LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbdense.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 844 1053 80.2 %
Date: 2024-05-14 13:00:50 Functions: 24 24 100.0 %

          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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include <cassert>
      30             : #include <limits>
      31             : 
      32             : #include "tiledbheaders.h"
      33             : 
      34             : constexpr const char *RASTER_DATASET_TYPE = "raster";
      35             : 
      36             : /************************************************************************/
      37             : /* ==================================================================== */
      38             : /*                            TileDBRasterBand                          */
      39             : /* ==================================================================== */
      40             : /************************************************************************/
      41             : 
      42             : class TileDBRasterBand final : public GDALPamRasterBand
      43             : {
      44             :     friend class TileDBRasterDataset;
      45             : 
      46             :   protected:
      47             :     TileDBRasterDataset *poGDS;
      48             :     bool bStats;
      49             :     CPLString osAttrName;
      50             : 
      51             :   public:
      52             :     TileDBRasterBand(TileDBRasterDataset *, int,
      53             :                      const std::string &osAttr = TILEDB_VALUES);
      54             :     virtual CPLErr IReadBlock(int, int, void *) override;
      55             :     virtual CPLErr IWriteBlock(int, int, void *) override;
      56             :     virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
      57             :                              GDALDataType, GSpacing, GSpacing,
      58             :                              GDALRasterIOExtraArg *psExtraArg) override;
      59             :     virtual GDALColorInterp GetColorInterpretation() override;
      60             : };
      61             : 
      62         153 : static CPLErr option_to_index_type(const char *pszIndexingType,
      63             :                                    TILEDB_INTERLEAVE_MODE &eMode)
      64             : {
      65         153 :     CPLErr eErr = CE_None;
      66             : 
      67         153 :     if (pszIndexingType)
      68             :     {
      69          80 :         if EQUAL (pszIndexingType, "BAND")
      70          53 :             eMode = BAND;
      71          27 :         else if EQUAL (pszIndexingType, "ATTRIBUTES")
      72           6 :             eMode = ATTRIBUTES;
      73          21 :         else if EQUAL (pszIndexingType, "PIXEL")
      74          21 :             eMode = PIXEL;
      75             :         else
      76             :         {
      77           0 :             eErr = CE_Failure;
      78           0 :             CPLError(eErr, CPLE_AppDefined,
      79             :                      "Unable to identify TileDB index mode %s.",
      80             :                      pszIndexingType);
      81             :         }
      82             :     }
      83             :     else
      84             :     {
      85          73 :         eMode = BAND;
      86             :     }
      87             : 
      88         153 :     return eErr;
      89             : }
      90             : 
      91          65 : static const char *index_type_name(TILEDB_INTERLEAVE_MODE eMode)
      92             : {
      93          65 :     switch (eMode)
      94             :     {
      95           7 :         case PIXEL:
      96           7 :             return "PIXEL";
      97           4 :         case ATTRIBUTES:
      98           4 :             return "ATTRIBUTES";
      99          54 :         case BAND:
     100          54 :             return "BAND";
     101           0 :         default:
     102           0 :             return nullptr;
     103             :     }
     104             : }
     105             : 
     106             : /************************************************************************/
     107             : /*                             SetBuffer()                              */
     108             : /************************************************************************/
     109             : 
     110         332 : static CPLErr SetBuffer(tiledb::Query *poQuery, GDALDataType eType,
     111             :                         const CPLString &osAttrName, void *pImage, int nSize)
     112             : {
     113         332 :     switch (eType)
     114             :     {
     115         136 :         case GDT_Byte:
     116             :             poQuery->set_data_buffer(
     117         136 :                 osAttrName, reinterpret_cast<unsigned char *>(pImage), nSize);
     118         136 :             break;
     119           2 :         case GDT_Int8:
     120             :             poQuery->set_data_buffer(osAttrName,
     121           2 :                                      reinterpret_cast<int8_t *>(pImage), nSize);
     122           2 :             break;
     123           3 :         case GDT_UInt16:
     124             :             poQuery->set_data_buffer(
     125           3 :                 osAttrName, reinterpret_cast<unsigned short *>(pImage), nSize);
     126           3 :             break;
     127           3 :         case GDT_UInt32:
     128             :             poQuery->set_data_buffer(
     129           3 :                 osAttrName, reinterpret_cast<unsigned int *>(pImage), nSize);
     130           3 :             break;
     131           2 :         case GDT_UInt64:
     132             :             poQuery->set_data_buffer(
     133           2 :                 osAttrName, reinterpret_cast<uint64_t *>(pImage), nSize);
     134           2 :             break;
     135           3 :         case GDT_Int16:
     136             :             poQuery->set_data_buffer(osAttrName,
     137           3 :                                      reinterpret_cast<short *>(pImage), nSize);
     138           3 :             break;
     139           6 :         case GDT_Int32:
     140             :             poQuery->set_data_buffer(osAttrName,
     141           6 :                                      reinterpret_cast<int *>(pImage), nSize);
     142           6 :             break;
     143           2 :         case GDT_Int64:
     144             :             poQuery->set_data_buffer(
     145           2 :                 osAttrName, reinterpret_cast<int64_t *>(pImage), nSize);
     146           2 :             break;
     147         156 :         case GDT_Float32:
     148             :             poQuery->set_data_buffer(osAttrName,
     149         156 :                                      reinterpret_cast<float *>(pImage), nSize);
     150         156 :             break;
     151           3 :         case GDT_Float64:
     152             :             poQuery->set_data_buffer(osAttrName,
     153           3 :                                      reinterpret_cast<double *>(pImage), nSize);
     154           3 :             break;
     155           3 :         case GDT_CInt16:
     156             :             poQuery->set_data_buffer(
     157           3 :                 osAttrName, reinterpret_cast<short *>(pImage), nSize * 2);
     158           3 :             break;
     159           3 :         case GDT_CInt32:
     160             :             poQuery->set_data_buffer(
     161           3 :                 osAttrName, reinterpret_cast<int *>(pImage), nSize * 2);
     162           3 :             break;
     163           3 :         case GDT_CFloat32:
     164             :             poQuery->set_data_buffer(
     165           3 :                 osAttrName, reinterpret_cast<float *>(pImage), nSize * 2);
     166           3 :             break;
     167           7 :         case GDT_CFloat64:
     168             :             poQuery->set_data_buffer(
     169           7 :                 osAttrName, reinterpret_cast<double *>(pImage), nSize * 2);
     170           7 :             break;
     171           0 :         default:
     172           0 :             return CE_Failure;
     173             :     }
     174         332 :     return CE_None;
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                          TileDBRasterBand()                          */
     179             : /************************************************************************/
     180             : 
     181         792 : TileDBRasterBand::TileDBRasterBand(TileDBRasterDataset *poDSIn, int nBandIn,
     182         792 :                                    const std::string &osAttr)
     183         792 :     : poGDS(poDSIn), bStats(poDSIn->bStats), osAttrName(osAttr)
     184             : {
     185         792 :     poDS = poDSIn;
     186         792 :     nBand = nBandIn;
     187         792 :     eDataType = poGDS->eDataType;
     188         792 :     if (eDataType == GDT_Unknown)
     189             :     {
     190             :         try
     191             :         {
     192          14 :             auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
     193          14 :                             ->schema()
     194          28 :                             .attribute(osAttr);
     195          14 :             switch (attr.type())
     196             :             {
     197           1 :                 case TILEDB_INT8:
     198           1 :                     eDataType = GDT_Int8;
     199           1 :                     break;
     200           1 :                 case TILEDB_UINT8:
     201           1 :                     eDataType = GDT_Byte;
     202           1 :                     break;
     203           2 :                 case TILEDB_INT16:
     204           2 :                     eDataType =
     205           2 :                         attr.cell_val_num() == 2 ? GDT_CInt16 : GDT_Int16;
     206           2 :                     break;
     207           1 :                 case TILEDB_UINT16:
     208           1 :                     eDataType = GDT_UInt16;
     209           1 :                     break;
     210           2 :                 case TILEDB_INT32:
     211           2 :                     eDataType =
     212           2 :                         attr.cell_val_num() == 2 ? GDT_CInt32 : GDT_Int32;
     213           2 :                     break;
     214           1 :                 case TILEDB_UINT32:
     215           1 :                     eDataType = GDT_UInt32;
     216           1 :                     break;
     217           1 :                 case TILEDB_INT64:
     218           1 :                     eDataType = GDT_Int64;
     219           1 :                     break;
     220           1 :                 case TILEDB_UINT64:
     221           1 :                     eDataType = GDT_UInt64;
     222           1 :                     break;
     223           2 :                 case TILEDB_FLOAT32:
     224           2 :                     eDataType =
     225           2 :                         attr.cell_val_num() == 2 ? GDT_CFloat32 : GDT_Float32;
     226           2 :                     break;
     227           2 :                 case TILEDB_FLOAT64:
     228           2 :                     eDataType =
     229           2 :                         attr.cell_val_num() == 2 ? GDT_CFloat64 : GDT_Float64;
     230           2 :                     break;
     231           0 :                 default:
     232             :                 {
     233           0 :                     const char *pszTypeName = "";
     234           0 :                     tiledb_datatype_to_str(attr.type(), &pszTypeName);
     235           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     236             :                              "Unhandled TileDB data type: %s", pszTypeName);
     237           0 :                     break;
     238             :                 }
     239             :             }
     240             :         }
     241           0 :         catch (const tiledb::TileDBError &e)
     242             :         {
     243           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     244             :         }
     245             :     }
     246         792 :     eAccess = poGDS->eAccess;
     247         792 :     nRasterXSize = poGDS->nRasterXSize;
     248         792 :     nRasterYSize = poGDS->nRasterYSize;
     249         792 :     nBlockXSize = poGDS->nBlockXSize;
     250         792 :     nBlockYSize = poGDS->nBlockYSize;
     251         792 : }
     252             : 
     253             : /************************************************************************/
     254             : /*                             IRasterIO()                              */
     255             : /************************************************************************/
     256             : 
     257         264 : CPLErr TileDBRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     258             :                                    int nXSize, int nYSize, void *pData,
     259             :                                    int nBufXSize, int nBufYSize,
     260             :                                    GDALDataType eBufType, GSpacing nPixelSpace,
     261             :                                    GSpacing nLineSpace,
     262             :                                    GDALRasterIOExtraArg *psExtraArg)
     263             : {
     264         264 :     if (poGDS->eIndexMode == ATTRIBUTES && eRWFlag == GF_Write)
     265             :     {
     266           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     267             :                  "Unable to write using band ordered IRasterIO when using "
     268             :                  "interleave 'ATTRIBUTES'.\n");
     269           0 :         return CE_Failure;
     270             :     }
     271             : 
     272         264 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
     273             : 
     274         264 :     if (eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
     275         216 :         nBufferDTSize > 0 && (nPixelSpace % nBufferDTSize) == 0 &&
     276         216 :         (nLineSpace % nBufferDTSize) == 0)
     277             :     {
     278         216 :         const uint64_t nBandIdx = poGDS->nBandStart + nBand - 1;
     279             :         std::vector<uint64_t> oaSubarray = {
     280             :             nBandIdx,        nBandIdx,
     281         216 :             (uint64_t)nYOff, (uint64_t)nYOff + nYSize - 1,
     282         432 :             (uint64_t)nXOff, (uint64_t)nXOff + nXSize - 1};
     283         216 :         if (poGDS->eIndexMode == PIXEL)
     284          78 :             std::rotate(oaSubarray.begin(), oaSubarray.begin() + 2,
     285          78 :                         oaSubarray.end());
     286             : 
     287             :         const bool bUseReadOnlyObjs =
     288         270 :             ((eRWFlag == GF_Read) && (eAccess == GA_Update) &&
     289          54 :              (poGDS->m_roArray));
     290         216 :         const auto &oCtxt = bUseReadOnlyObjs ? *poGDS->m_roCtx : *poGDS->m_ctx;
     291             :         const auto &oArray =
     292         216 :             bUseReadOnlyObjs ? *poGDS->m_roArray : *poGDS->m_array;
     293             : 
     294         432 :         auto poQuery = std::make_unique<tiledb::Query>(oCtxt, oArray);
     295         432 :         tiledb::Subarray subarray(oCtxt, oArray);
     296         216 :         if (poGDS->m_array->schema().domain().ndim() == 3)
     297             :         {
     298         149 :             subarray.set_subarray(oaSubarray);
     299             :         }
     300             :         else
     301             :         {
     302         134 :             subarray.set_subarray(std::vector<uint64_t>(oaSubarray.cbegin() + 2,
     303          67 :                                                         oaSubarray.cend()));
     304             :         }
     305         216 :         poQuery->set_subarray(subarray);
     306             : 
     307         216 :         SetBuffer(poQuery.get(), eDataType, osAttrName, pData, nXSize * nYSize);
     308             : 
     309             :         // write additional co-registered values
     310         432 :         std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
     311             : 
     312         216 :         if (poGDS->lpoAttributeDS.size() > 0)
     313             :         {
     314          10 :             for (auto const &poAttrDS : poGDS->lpoAttributeDS)
     315             :             {
     316           7 :                 GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(nBand);
     317           7 :                 GDALDataType eAttrType = poAttrBand->GetRasterDataType();
     318           7 :                 int nBytes = GDALGetDataTypeSizeBytes(eAttrType);
     319           7 :                 size_t nValues = static_cast<size_t>(nBufXSize) * nBufYSize;
     320           7 :                 void *pAttrBlock = VSIMalloc(nBytes * nValues);
     321           7 :                 aBlocks.emplace_back(pAttrBlock, &VSIFree);
     322             : 
     323           7 :                 if (pAttrBlock == nullptr)
     324             :                 {
     325           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
     326             :                              "Cannot allocate attribute buffer");
     327           1 :                     return CE_Failure;
     328             :                 }
     329             : 
     330           7 :                 poAttrBand->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize,
     331           7 :                                        nBufYSize, eAttrType, nullptr);
     332             : 
     333           7 :                 CPLErr eErr = poAttrBand->RasterIO(
     334             :                     GF_Read, nXOff, nYOff, nXSize, nYSize, pAttrBlock,
     335             :                     nBufXSize, nBufYSize, eAttrType, nPixelSpace, nLineSpace,
     336             :                     psExtraArg);
     337             : 
     338           7 :                 if (eErr == CE_None)
     339             :                 {
     340           6 :                     CPLString osName = CPLString().Printf(
     341          12 :                         "%s", CPLGetBasename(poAttrDS->GetDescription()));
     342             : 
     343           6 :                     SetBuffer(poQuery.get(), eAttrType, osName, pAttrBlock,
     344             :                               nBufXSize * nBufYSize);
     345             :                 }
     346             :                 else
     347             :                 {
     348           1 :                     return eErr;
     349             :                 }
     350             :             }
     351             :         }
     352             : 
     353         215 :         if (bStats)
     354           0 :             tiledb::Stats::enable();
     355             : 
     356         215 :         auto status = poQuery->submit();
     357             : 
     358         215 :         if (bStats)
     359             :         {
     360           0 :             tiledb::Stats::dump(stdout);
     361           0 :             tiledb::Stats::disable();
     362             :         }
     363             : 
     364         215 :         if (status == tiledb::Query::Status::FAILED)
     365           0 :             return CE_Failure;
     366             :         else
     367         215 :             return CE_None;
     368             :     }
     369             : 
     370          48 :     return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     371             :                                         pData, nBufXSize, nBufYSize, eBufType,
     372          48 :                                         nPixelSpace, nLineSpace, psExtraArg);
     373             : }
     374             : 
     375         124 : CPLErr TileDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     376             :                                     void *pImage)
     377             : {
     378         124 :     const int nXOff = nBlockXOff * nBlockXSize;
     379         124 :     const int nYOff = nBlockYOff * nBlockYSize;
     380         124 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     381         248 :     return IRasterIO(GF_Read, nXOff, nYOff, nBlockXSize, nBlockYSize, pImage,
     382             :                      nBlockXSize, nBlockYSize, eDataType, nDTSize,
     383         124 :                      nDTSize * nBlockXSize, nullptr);
     384             : }
     385             : 
     386             : /************************************************************************/
     387             : /*                             IWriteBlock()                            */
     388             : /************************************************************************/
     389             : 
     390          21 : CPLErr TileDBRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
     391             :                                      void *pImage)
     392             : 
     393             : {
     394          21 :     if (eAccess == GA_ReadOnly)
     395             :     {
     396           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     397             :                  "Unable to write block, dataset is opened read only.\n");
     398           0 :         return CE_Failure;
     399             :     }
     400             : 
     401          21 :     CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     402             :               pImage != nullptr);
     403             : 
     404          21 :     int nStartX = nBlockXSize * nBlockXOff;
     405          21 :     int nStartY = nBlockYSize * nBlockYOff;
     406             : 
     407          21 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     408          42 :     return IRasterIO(GF_Write, nStartX, nStartY, nBlockXSize, nBlockYSize,
     409             :                      pImage, nBlockXSize, nBlockYSize, eDataType, nDTSize,
     410          21 :                      nDTSize * nBlockXSize, nullptr);
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                       GetColorInterpretation()                       */
     415             : /************************************************************************/
     416             : 
     417          49 : GDALColorInterp TileDBRasterBand::GetColorInterpretation()
     418             : 
     419             : {
     420          49 :     if (poGDS->nBands == 1)
     421          19 :         return GCI_GrayIndex;
     422             : 
     423          30 :     if (nBand == 1)
     424          10 :         return GCI_RedBand;
     425             : 
     426          20 :     else if (nBand == 2)
     427          10 :         return GCI_GreenBand;
     428             : 
     429          10 :     else if (nBand == 3)
     430          10 :         return GCI_BlueBand;
     431             : 
     432           0 :     return GCI_AlphaBand;
     433             : }
     434             : 
     435             : /************************************************************************/
     436             : /* ==================================================================== */
     437             : /*                     TileDBRasterDataset                              */
     438             : /* ==================================================================== */
     439             : /************************************************************************/
     440             : 
     441             : /************************************************************************/
     442             : /*                     ~TileDBRasterDataset()                           */
     443             : /************************************************************************/
     444             : 
     445         350 : TileDBRasterDataset::~TileDBRasterDataset()
     446             : 
     447             : {
     448         175 :     TileDBRasterDataset::FlushCache(true);
     449             : 
     450             :     try
     451             :     {
     452         175 :         if (m_array)
     453             :         {
     454         162 :             m_array->close();
     455             :         }
     456             :     }
     457           0 :     catch (const tiledb::TileDBError &e)
     458             :     {
     459           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     460             :     }
     461         175 :     CPLDestroyXMLNode(psSubDatasetsTree);
     462         175 :     CSLDestroy(papszSubDatasets);
     463         175 :     CSLDestroy(papszAttributes);
     464         350 : }
     465             : 
     466             : /************************************************************************/
     467             : /*                           IRasterio()                                */
     468             : /************************************************************************/
     469             : 
     470          76 : CPLErr TileDBRasterDataset::IRasterIO(
     471             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     472             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     473             :     int nBandCount, int *panBandMap, GSpacing nPixelSpace, GSpacing nLineSpace,
     474             :     GSpacing nBandSpace, CPL_UNUSED GDALRasterIOExtraArg *psExtraArg)
     475             : 
     476             : {
     477             :     // support special case of writing attributes for bands, all attributes have to be set at once
     478          76 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
     479             : 
     480          76 :     if (eIndexMode == ATTRIBUTES && nBandCount == nBands &&
     481           4 :         eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
     482           4 :         nBufferDTSize > 0 && (nPixelSpace % nBufferDTSize) == 0 &&
     483           4 :         (nLineSpace % nBufferDTSize) == 0)
     484             :     {
     485             :         std::vector<uint64_t> oaSubarray = {
     486           4 :             (uint64_t)nYOff, (uint64_t)nYOff + nYSize - 1, (uint64_t)nXOff,
     487           8 :             (uint64_t)nXOff + nXSize - 1};
     488             : 
     489             :         const bool bUseReadOnlyObjs =
     490           4 :             ((eRWFlag == GF_Read) && (eAccess == GA_Update) && (m_roArray));
     491           4 :         const auto &oCtxt = bUseReadOnlyObjs ? *m_roCtx : *m_ctx;
     492           4 :         const auto &oArray = bUseReadOnlyObjs ? *m_roArray : *m_array;
     493             : 
     494           8 :         auto poQuery = std::make_unique<tiledb::Query>(oCtxt, oArray);
     495           8 :         tiledb::Subarray subarray(oCtxt, oArray);
     496           4 :         subarray.set_subarray(oaSubarray);
     497           4 :         poQuery->set_subarray(subarray);
     498             : 
     499          14 :         for (int b = 0; b < nBandCount; b++)
     500             :         {
     501             :             TileDBRasterBand *poBand =
     502          10 :                 (TileDBRasterBand *)GetRasterBand(panBandMap[b]);
     503          10 :             int nRegionSize = nBufXSize * nBufYSize * nBufferDTSize;
     504          10 :             SetBuffer(poQuery.get(), eDataType, poBand->osAttrName,
     505          10 :                       ((GByte *)pData) + b * nRegionSize, nRegionSize);
     506             :         }
     507             : 
     508           4 :         if (bStats)
     509           0 :             tiledb::Stats::enable();
     510             : 
     511           4 :         auto status = poQuery->submit();
     512             : 
     513           4 :         if (bStats)
     514             :         {
     515           0 :             tiledb::Stats::dump(stdout);
     516           0 :             tiledb::Stats::disable();
     517             :         }
     518             : 
     519           4 :         if (status == tiledb::Query::Status::FAILED)
     520           0 :             return CE_Failure;
     521             :         else
     522           4 :             return CE_None;
     523             :     }
     524             : 
     525          72 :     return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     526             :                                      pData, nBufXSize, nBufYSize, eBufType,
     527             :                                      nBandCount, panBandMap, nPixelSpace,
     528          72 :                                      nLineSpace, nBandSpace, psExtraArg);
     529             : }
     530             : 
     531             : /************************************************************************/
     532             : /*                             AddDimensions()                          */
     533             : /************************************************************************/
     534             : 
     535          93 : CPLErr TileDBRasterDataset::AddDimensions(tiledb::Domain &domain,
     536             :                                           const char *pszAttrName,
     537             :                                           tiledb::Dimension &y,
     538             :                                           tiledb::Dimension &x,
     539             :                                           tiledb::Dimension *poBands)
     540             : 
     541             : {
     542          93 :     CPLErr eErr = CE_None;
     543          93 :     switch (eIndexMode)
     544             :     {
     545           5 :         case ATTRIBUTES:
     546           5 :             domain.add_dimensions(y, x);
     547           5 :             CreateAttribute(eDataType, pszAttrName, nBands);
     548           5 :             break;
     549           7 :         case PIXEL:
     550           7 :             assert(poBands);
     551           7 :             domain.add_dimensions(y, x, *poBands);
     552           7 :             CreateAttribute(eDataType, pszAttrName, 1);
     553           7 :             break;
     554          81 :         default:  // BAND
     555          81 :             assert(poBands);
     556          81 :             domain.add_dimensions(*poBands, y, x);
     557          81 :             CreateAttribute(eDataType, pszAttrName, 1);
     558          81 :             break;
     559             :     }
     560             : 
     561          93 :     return eErr;
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                             FlushCache()                             */
     566             : /************************************************************************/
     567             : 
     568         196 : CPLErr TileDBRasterDataset::FlushCache(bool bAtClosing)
     569             : 
     570             : {
     571         196 :     const CPLErr eErr = BlockBasedFlushCache(bAtClosing);
     572             : 
     573         196 :     if (nPamFlags & GPF_DIRTY)
     574          91 :         TrySaveXML();
     575         196 :     return eErr;
     576             : }
     577             : 
     578             : /************************************************************************/
     579             : /*                           TrySaveXML()                               */
     580             : /************************************************************************/
     581             : 
     582          91 : CPLErr TileDBRasterDataset::TrySaveXML()
     583             : 
     584             : {
     585          91 :     if (m_array == nullptr)
     586           0 :         return CE_None;
     587             : 
     588          91 :     CPLXMLNode *psTree = nullptr;
     589             :     try
     590             :     {
     591         182 :         tiledb::VFS vfs(*m_ctx, m_ctx->config());
     592             : 
     593          91 :         nPamFlags &= ~GPF_DIRTY;
     594             : 
     595          91 :         if (psPam == nullptr || (nPamFlags & GPF_NOSAVE))
     596           0 :             return CE_None;
     597             : 
     598             :         /* --------------------------------------------------------------------
     599             :          */
     600             :         /*      Make sure we know the filename we want to store in. */
     601             :         /* --------------------------------------------------------------------
     602             :          */
     603          91 :         if (!BuildPamFilename())
     604           0 :             return CE_None;
     605             : 
     606             :         /* --------------------------------------------------------------------
     607             :          */
     608             :         /*      Build the XML representation of the auxiliary metadata. */
     609             :         /* --------------------------------------------------------------------
     610             :          */
     611          91 :         psTree = SerializeToXML(nullptr);
     612             : 
     613          91 :         if (psTree == nullptr)
     614             :         {
     615             :             /* If we have unset all metadata, we have to delete the PAM file */
     616           0 :             m_array->delete_metadata(GDAL_ATTRIBUTE_NAME);
     617           0 :             return CE_None;
     618             :         }
     619             : 
     620          91 :         if (psSubDatasetsTree != nullptr)
     621             :         {
     622           3 :             CPLAddXMLChild(psTree, CPLCloneXMLTree(psSubDatasetsTree->psChild));
     623             :         }
     624             : 
     625             :         /* --------------------------------------------------------------------
     626             :          */
     627             :         /*      If we are working with a subdataset, we need to integrate */
     628             :         /*      the subdataset tree within the whole existing pam tree, */
     629             :         /*      after removing any old version of the same subdataset. */
     630             :         /* --------------------------------------------------------------------
     631             :          */
     632          91 :         if (!psPam->osSubdatasetName.empty())
     633             :         {
     634             :             CPLXMLNode *psOldTree, *psSubTree;
     635             : 
     636           0 :             CPLErrorReset();
     637             :             {
     638           0 :                 CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
     639           0 :                 psOldTree = CPLParseXMLFile(psPam->pszPamFilename);
     640             :             }
     641             : 
     642           0 :             if (psOldTree == nullptr)
     643             :                 psOldTree =
     644           0 :                     CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset");
     645             : 
     646           0 :             for (psSubTree = psOldTree->psChild; psSubTree != nullptr;
     647           0 :                  psSubTree = psSubTree->psNext)
     648             :             {
     649           0 :                 if (psSubTree->eType != CXT_Element ||
     650           0 :                     !EQUAL(psSubTree->pszValue, "Subdataset"))
     651           0 :                     continue;
     652             : 
     653           0 :                 if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
     654             :                            psPam->osSubdatasetName))
     655           0 :                     continue;
     656             : 
     657           0 :                 break;
     658             :             }
     659             : 
     660           0 :             if (psSubTree == nullptr)
     661             :             {
     662             :                 psSubTree =
     663           0 :                     CPLCreateXMLNode(psOldTree, CXT_Element, "Subdataset");
     664           0 :                 CPLCreateXMLNode(
     665             :                     CPLCreateXMLNode(psSubTree, CXT_Attribute, "name"),
     666           0 :                     CXT_Text, psPam->osSubdatasetName);
     667             :             }
     668             : 
     669             :             CPLXMLNode *psOldPamDataset =
     670           0 :                 CPLGetXMLNode(psSubTree, "PAMDataset");
     671           0 :             if (psOldPamDataset != nullptr)
     672             :             {
     673           0 :                 CPLRemoveXMLChild(psSubTree, psOldPamDataset);
     674           0 :                 CPLDestroyXMLNode(psOldPamDataset);
     675             :             }
     676             : 
     677           0 :             CPLAddXMLChild(psSubTree, psTree);
     678           0 :             psTree = psOldTree;
     679             :         }
     680             : 
     681             :         /* --------------------------------------------------------------------
     682             :          */
     683             :         /*      Try saving the auxiliary metadata. */
     684             :         /* --------------------------------------------------------------------
     685             :          */
     686          91 :         CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
     687          91 :         char *pszTree = CPLSerializeXMLTree(psTree);
     688             : 
     689          91 :         if (eAccess == GA_ReadOnly)
     690             :         {
     691          40 :             if (nTimestamp)
     692             :             {
     693             :                 auto oMeta = std::unique_ptr<tiledb::Array>(new tiledb::Array(
     694           0 :                     *m_ctx, m_array->uri(), TILEDB_WRITE,
     695           0 :                     tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
     696           0 :                 oMeta->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
     697           0 :                                     static_cast<int>(strlen(pszTree)), pszTree);
     698           0 :                 oMeta->close();
     699             :             }
     700             :             else
     701             :             {
     702             :                 auto oMeta = std::unique_ptr<tiledb::Array>(
     703          80 :                     new tiledb::Array(*m_ctx, m_array->uri(), TILEDB_WRITE));
     704          80 :                 oMeta->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
     705          40 :                                     static_cast<int>(strlen(pszTree)), pszTree);
     706          40 :                 oMeta->close();
     707             :             }
     708             :         }
     709             :         else
     710             :         {
     711          51 :             m_array->put_metadata("dataset_type", TILEDB_STRING_UTF8,
     712             :                                   static_cast<int>(strlen(RASTER_DATASET_TYPE)),
     713             :                                   RASTER_DATASET_TYPE);
     714         102 :             m_array->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
     715          51 :                                   static_cast<int>(strlen(pszTree)), pszTree);
     716             :         }
     717             : 
     718          91 :         CPLFree(pszTree);
     719             : 
     720             :         /* --------------------------------------------------------------------
     721             :          */
     722             :         /*      Cleanup */
     723             :         /* --------------------------------------------------------------------
     724             :          */
     725          91 :         if (psTree)
     726          91 :             CPLDestroyXMLNode(psTree);
     727             : 
     728          91 :         return CE_None;
     729             :     }
     730           0 :     catch (const std::exception &e)
     731             :     {
     732           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     733           0 :         if (psTree)
     734           0 :             CPLDestroyXMLNode(psTree);
     735           0 :         return CE_Failure;
     736             :     }
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                           TryLoadXML()                               */
     741             : /************************************************************************/
     742             : 
     743          82 : CPLErr TileDBRasterDataset::TryLoadXML(char **papszSiblingFiles)
     744             : 
     745             : {
     746          82 :     return TryLoadCachedXML(papszSiblingFiles, true);
     747             : }
     748             : 
     749             : /************************************************************************/
     750             : /*                           TryLoadCachedXML()                               */
     751             : /************************************************************************/
     752             : 
     753         163 : CPLErr TileDBRasterDataset::TryLoadCachedXML(char ** /*papszSiblingFiles*/,
     754             :                                              bool bReload)
     755             : 
     756             : {
     757         163 :     CPLXMLNode *psTree = nullptr;
     758             :     try
     759             :     {
     760         163 :         PamInitialize();
     761         326 :         tiledb::VFS vfs(*m_ctx, m_ctx->config());
     762             : 
     763             :         /* --------------------------------------------------------------------
     764             :          */
     765             :         /*      Clear dirty flag.  Generally when we get to this point is */
     766             :         /*      from a call at the end of the Open() method, and some calls */
     767             :         /*      may have already marked the PAM info as dirty (for instance */
     768             :         /*      setting metadata), but really everything to this point is */
     769             :         /*      reproducible, and so the PAM info should not really be */
     770             :         /*      thought of as dirty. */
     771             :         /* --------------------------------------------------------------------
     772             :          */
     773         163 :         nPamFlags &= ~GPF_DIRTY;
     774             : 
     775             :         /* --------------------------------------------------------------------
     776             :          */
     777             :         /*      Try reading the file. */
     778             :         /* --------------------------------------------------------------------
     779             :          */
     780         163 :         if (!BuildPamFilename())
     781           0 :             return CE_None;
     782             : 
     783             :         /* --------------------------------------------------------------------
     784             :          */
     785             :         /*      In case the PAM filename is a .aux.xml file next to the */
     786             :         /*      physical file and we have a siblings list, then we can skip */
     787             :         /*      stat'ing the filesystem. */
     788             :         /* --------------------------------------------------------------------
     789             :          */
     790         163 :         CPLErr eLastErr = CPLGetLastErrorType();
     791         163 :         int nLastErrNo = CPLGetLastErrorNo();
     792         326 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     793             : 
     794         163 :         CPLErrorReset();
     795             :         {
     796         326 :             CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
     797             : 
     798         163 :             if (bReload)
     799             :             {
     800          82 :                 tiledb_datatype_t v_type =
     801             :                     TILEDB_UINT8;  // CPLSerializeXMLTree returns char*
     802          82 :                 const void *v_r = nullptr;
     803          82 :                 uint32_t v_num = 0;
     804          82 :                 if ((eAccess == GA_Update) && (m_roArray))
     805             :                 {
     806          13 :                     m_roArray->get_metadata(GDAL_ATTRIBUTE_NAME, &v_type,
     807             :                                             &v_num, &v_r);
     808          13 :                     if (v_r)
     809             :                     {
     810             :                         osMetaDoc =
     811          13 :                             CPLString(static_cast<const char *>(v_r), v_num);
     812             :                     }
     813             :                 }
     814             :                 else
     815             :                 {
     816          69 :                     m_array->get_metadata(GDAL_ATTRIBUTE_NAME, &v_type, &v_num,
     817             :                                           &v_r);
     818             : 
     819          69 :                     if (v_r)
     820             :                     {
     821             :                         osMetaDoc =
     822          68 :                             CPLString(static_cast<const char *>(v_r), v_num);
     823             :                     }
     824             :                 }
     825          82 :                 psTree = CPLParseXMLString(osMetaDoc);
     826             :             }
     827             : 
     828         164 :             if (bReload && psTree == nullptr &&
     829         164 :                 vfs.is_file(psPam->pszPamFilename))
     830             :             {
     831           1 :                 auto nBytes = vfs.file_size(psPam->pszPamFilename);
     832           2 :                 tiledb::VFS::filebuf fbuf(vfs);
     833           1 :                 fbuf.open(psPam->pszPamFilename, std::ios::in);
     834           1 :                 std::istream is(&fbuf);
     835           1 :                 osMetaDoc.resize(nBytes);
     836           1 :                 is.read((char *)osMetaDoc.data(), nBytes);
     837           1 :                 fbuf.close();
     838           1 :                 psTree = CPLParseXMLString(osMetaDoc);
     839             :             }
     840             : 
     841         163 :             if (!bReload)
     842             :             {
     843          81 :                 psTree = CPLParseXMLString(osMetaDoc);
     844             :             }
     845             :         }
     846         163 :         CPLErrorReset();
     847             : 
     848         163 :         if (eLastErr != CE_None)
     849           0 :             CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
     850             : 
     851             :         /* --------------------------------------------------------------------
     852             :          */
     853             :         /*      If we are looking for a subdataset, search for its subtree not.
     854             :          */
     855             :         /* --------------------------------------------------------------------
     856             :          */
     857         163 :         if (psTree && !psPam->osSubdatasetName.empty())
     858             :         {
     859          11 :             CPLXMLNode *psSubTree = psTree->psChild;
     860             : 
     861          57 :             for (; psSubTree != nullptr; psSubTree = psSubTree->psNext)
     862             :             {
     863          56 :                 if (psSubTree->eType != CXT_Element ||
     864          56 :                     !EQUAL(psSubTree->pszValue, "Subdataset"))
     865          38 :                     continue;
     866             : 
     867          18 :                 if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
     868             :                            psPam->osSubdatasetName))
     869           8 :                     continue;
     870             : 
     871          10 :                 psSubTree = CPLGetXMLNode(psSubTree, "PAMDataset");
     872          10 :                 break;
     873             :             }
     874             : 
     875          11 :             if (psSubTree != nullptr)
     876          10 :                 psSubTree = CPLCloneXMLTree(psSubTree);
     877             : 
     878          11 :             CPLDestroyXMLNode(psTree);
     879          11 :             psTree = psSubTree;
     880             :         }
     881         163 :         if (psTree == nullptr)
     882           1 :             return CE_Failure;
     883             : 
     884             :         /* --------------------------------------------------------------------
     885             :          */
     886             :         /*      Initialize ourselves from this XML tree. */
     887             :         /* --------------------------------------------------------------------
     888             :          */
     889             : 
     890         162 :         CPLString osPath(CPLGetPath(psPam->pszPamFilename));
     891         162 :         const CPLErr eErr = XMLInit(psTree, osPath);
     892             : 
     893         162 :         CPLDestroyXMLNode(psTree);
     894             : 
     895         162 :         if (eErr != CE_None)
     896           0 :             PamClear();
     897             : 
     898         162 :         return eErr;
     899             :     }
     900           0 :     catch (const tiledb::TileDBError &e)
     901             :     {
     902           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     903           0 :         if (psTree)
     904           0 :             CPLDestroyXMLNode(psTree);
     905           0 :         return CE_Failure;
     906             :     }
     907             : }
     908             : 
     909             : /************************************************************************/
     910             : /*                             GetMetadata()                            */
     911             : /************************************************************************/
     912             : 
     913         164 : char **TileDBRasterDataset::GetMetadata(const char *pszDomain)
     914             : 
     915             : {
     916         164 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     917             :     {
     918           3 :         char **papszMeta = CSLDuplicate(GDALPamDataset::GetMetadata(pszDomain));
     919           3 :         for (int i = 0; papszMeta && papszMeta[i]; i++)
     920             :         {
     921           0 :             if (STARTS_WITH(papszMeta[i], "SUBDATASET_") &&
     922           0 :                 strstr(papszMeta[i], "_NAME="))
     923             :             {
     924           0 :                 char *pszKey = nullptr;
     925           0 :                 const char *pszAttr = CPLParseNameValue(papszMeta[i], &pszKey);
     926           0 :                 if (pszAttr && !STARTS_WITH(pszAttr, "TILEDB:"))
     927             :                 {
     928           0 :                     CPLString osAttr(pszAttr);
     929           0 :                     CPLFree(papszMeta[i]);
     930           0 :                     papszMeta[i] = CPLStrdup(
     931           0 :                         CPLString().Printf("%s=TILEDB:\"%s\":%s", pszKey,
     932           0 :                                            GetDescription(), osAttr.c_str()));
     933             :                 }
     934           0 :                 CPLFree(pszKey);
     935             :             }
     936             :         }
     937           3 :         m_osSubdatasetMD.Assign(papszMeta);
     938           3 :         return m_osSubdatasetMD.List();
     939             :     }
     940             :     else
     941             :     {
     942         161 :         return GDALPamDataset::GetMetadata(pszDomain);
     943             :     }
     944             : }
     945             : 
     946             : /************************************************************************/
     947             : /*                                Open()                                */
     948             : /************************************************************************/
     949             : 
     950          82 : GDALDataset *TileDBRasterDataset::Open(GDALOpenInfo *poOpenInfo)
     951             : 
     952             : {
     953         164 :     auto poDS = std::make_unique<TileDBRasterDataset>();
     954             : 
     955             :     const char *pszConfig =
     956          82 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
     957             : 
     958             :     const char *pszTimestamp =
     959          82 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_TIMESTAMP");
     960             : 
     961         164 :     poDS->bStats =
     962          82 :         CSLFetchBoolean(poOpenInfo->papszOpenOptions, "STATS", FALSE);
     963             : 
     964          82 :     if (pszConfig != nullptr)
     965             :     {
     966           0 :         tiledb::Config cfg(pszConfig);
     967           0 :         poDS->m_ctx.reset(new tiledb::Context(cfg));
     968             :     }
     969             :     else
     970             :     {
     971          82 :         poDS->m_ctx.reset(new tiledb::Context());
     972             :     }
     973          82 :     if (pszTimestamp)
     974          18 :         poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
     975             : 
     976         164 :     CPLString osArrayPath;
     977         164 :     CPLString osAux;
     978         164 :     CPLString osSubdataset;
     979             : 
     980         164 :     std::string osAttrNameTmp;
     981             :     const char *pszAttr =
     982          82 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_ATTRIBUTE");
     983             : 
     984          82 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
     985           2 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
     986             :     {
     987             :         // form required read attributes and open file
     988             :         // Create a corresponding GDALDataset.
     989             :         CPLStringList apszName(
     990           2 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     991           2 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
     992             : 
     993           2 :         if (apszName.size() != 3)
     994             :         {
     995           0 :             return nullptr;
     996             :         }
     997             : 
     998           2 :         osArrayPath = TileDBDataset::VSI_to_tiledb_uri(apszName[1]);
     999           2 :         osSubdataset = apszName[2];
    1000           4 :         poDS->SetSubdatasetName(osSubdataset.c_str());
    1001             :     }
    1002             :     else
    1003             :     {
    1004          80 :         if (pszAttr != nullptr)
    1005             :         {
    1006           4 :             poDS->SetSubdatasetName(pszAttr);
    1007             :         }
    1008             : 
    1009          80 :         osArrayPath = TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
    1010             :     }
    1011             : 
    1012          82 :     const char *pszArrayName = CPLGetBasename(osArrayPath);
    1013          82 :     osAux.Printf("%s.tdb", pszArrayName);
    1014             : 
    1015             :     // aux file is in array folder
    1016          82 :     poDS->SetPhysicalFilename(CPLFormFilename(osArrayPath, osAux, nullptr));
    1017             :     // Initialize any PAM information.
    1018          82 :     poDS->SetDescription(osArrayPath);
    1019             : 
    1020          82 :     tiledb_query_type_t eMode = TILEDB_READ;
    1021          82 :     if (poOpenInfo->eAccess == GA_Update)
    1022             :     {
    1023          13 :         eMode = TILEDB_WRITE;
    1024          13 :         poDS->m_roCtx.reset(new tiledb::Context(poDS->m_ctx->config()));
    1025          39 :         poDS->m_roArray.reset(
    1026          26 :             new tiledb::Array(*poDS->m_roCtx, osArrayPath, TILEDB_READ));
    1027             :     }
    1028             : 
    1029          82 :     if (poDS->nTimestamp)
    1030             :     {
    1031          18 :         if (eMode == TILEDB_READ)
    1032             :         {
    1033          30 :             poDS->m_array.reset(new tiledb::Array(
    1034          10 :                 *poDS->m_ctx, osArrayPath, TILEDB_READ,
    1035          30 :                 tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp)));
    1036             :         }
    1037             :         else
    1038             :         {
    1039          24 :             poDS->m_array.reset(new tiledb::Array(
    1040           8 :                 *poDS->m_ctx, osArrayPath, TILEDB_WRITE,
    1041          24 :                 tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp)));
    1042             :         }
    1043             :     }
    1044             :     else
    1045         192 :         poDS->m_array.reset(
    1046         128 :             new tiledb::Array(*poDS->m_ctx, osArrayPath, eMode));
    1047             : 
    1048          82 :     poDS->eAccess = poOpenInfo->eAccess;
    1049             : 
    1050             :     // dependent on PAM metadata for information about array
    1051          82 :     poDS->TryLoadXML();
    1052             : 
    1053         164 :     tiledb::ArraySchema schema = poDS->m_array->schema();
    1054             : 
    1055          82 :     char **papszStructMeta = poDS->GetMetadata("IMAGE_STRUCTURE");
    1056          82 :     const char *pszXSize = CSLFetchNameValue(papszStructMeta, "X_SIZE");
    1057          82 :     if (pszXSize)
    1058             :     {
    1059          67 :         poDS->nRasterXSize = atoi(pszXSize);
    1060          67 :         if (poDS->nRasterXSize <= 0)
    1061             :         {
    1062           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1063             :                      "Width %i should be greater than zero.",
    1064           0 :                      poDS->nRasterXSize);
    1065           0 :             return nullptr;
    1066             :         }
    1067             :     }
    1068             : 
    1069          82 :     const char *pszYSize = CSLFetchNameValue(papszStructMeta, "Y_SIZE");
    1070          82 :     if (pszYSize)
    1071             :     {
    1072          67 :         poDS->nRasterYSize = atoi(pszYSize);
    1073          67 :         if (poDS->nRasterYSize <= 0)
    1074             :         {
    1075           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1076             :                      "Height %i should be greater than zero.",
    1077           0 :                      poDS->nRasterYSize);
    1078           0 :             return nullptr;
    1079             :         }
    1080             :     }
    1081             : 
    1082          82 :     const char *pszNBits = CSLFetchNameValue(papszStructMeta, "NBITS");
    1083          82 :     if (pszNBits)
    1084             :     {
    1085          67 :         poDS->nBitsPerSample = atoi(pszNBits);
    1086             :     }
    1087             : 
    1088          82 :     const char *pszDataType = CSLFetchNameValue(papszStructMeta, "DATA_TYPE");
    1089          82 :     if (pszDataType)
    1090             :     {
    1091             :         // handle the case where arrays have been written with int type
    1092             :         // (2.5.0)
    1093          67 :         GDALDataType eDT = GDALGetDataTypeByName(pszDataType);
    1094          67 :         if (eDT == GDT_Unknown)
    1095             :         {
    1096           1 :             int t = atoi(pszDataType);
    1097           1 :             if ((t > 0) && (t < GDT_TypeCount))
    1098           1 :                 poDS->eDataType = static_cast<GDALDataType>(atoi(pszDataType));
    1099             :             else
    1100             :             {
    1101           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Unknown data type %s.",
    1102             :                          pszDataType);
    1103           0 :                 return nullptr;
    1104             :             }
    1105             :         }
    1106             :         else
    1107             :         {
    1108          66 :             poDS->eDataType = eDT;
    1109             :         }
    1110             :     }
    1111             :     else
    1112             :     {
    1113          15 :         if (!pszAttr)
    1114             :         {
    1115          15 :             if (schema.attribute_num() == 1)
    1116             :             {
    1117          14 :                 osAttrNameTmp = schema.attribute(0).name();
    1118          14 :                 pszAttr = osAttrNameTmp.c_str();
    1119             :             }
    1120             :         }
    1121             :     }
    1122             : 
    1123          82 :     const char *pszIndexMode = CSLFetchNameValue(papszStructMeta, "INTERLEAVE");
    1124             : 
    1125          82 :     if (pszIndexMode)
    1126          61 :         option_to_index_type(pszIndexMode, poDS->eIndexMode);
    1127             : 
    1128         164 :     std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
    1129             : 
    1130          82 :     int iYDim = 0;
    1131          82 :     int iXDim = 1;
    1132          82 :     if ((dims.size() == 2) || (dims.size() == 3))
    1133             :     {
    1134          82 :         if (dims.size() == 3)
    1135             :         {
    1136          96 :             if ((pszAttr != nullptr) &&
    1137          96 :                 (schema.attributes().count(pszAttr) == 0))
    1138             :             {
    1139           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1140             :                          "%s attribute is not found in TileDB schema.",
    1141             :                          pszAttr);
    1142           0 :                 return nullptr;
    1143             :             }
    1144             : 
    1145          78 :             if (poDS->eIndexMode == PIXEL)
    1146          14 :                 std::rotate(dims.begin(), dims.begin() + 2, dims.end());
    1147             : 
    1148          78 :             if (dims[0].type() != TILEDB_UINT64)
    1149             :             {
    1150           0 :                 const char *pszTypeName = "";
    1151           0 :                 tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1152           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1153             :                          "Unsupported BAND dimension type: %s", pszTypeName);
    1154           0 :                 return nullptr;
    1155             :             }
    1156          78 :             poDS->nBandStart = dims[0].domain<uint64_t>().first;
    1157          78 :             const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
    1158         156 :             if (nBandEnd < poDS->nBandStart ||
    1159          78 :                 nBandEnd - poDS->nBandStart >
    1160          78 :                     static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1161             :             {
    1162           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1163             :                          "Invalid bounds for BAND dimension.");
    1164           0 :                 return nullptr;
    1165             :             }
    1166          78 :             poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
    1167          78 :             iYDim = 1;
    1168          78 :             iXDim = 2;
    1169             :         }
    1170             :         else
    1171             :         {
    1172             :             const char *pszBands =
    1173           4 :                 poDS->GetMetadataItem("NUM_BANDS", "IMAGE_STRUCTURE");
    1174           4 :             if (pszBands)
    1175             :             {
    1176           1 :                 poDS->nBands = atoi(pszBands);
    1177             :             }
    1178             : 
    1179           4 :             poDS->eIndexMode = ATTRIBUTES;
    1180             :         }
    1181             :     }
    1182             :     else
    1183             :     {
    1184           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1185             :                  "Wrong number of dimensions %d: expected 2 or 3.",
    1186           0 :                  static_cast<int>(dims.size()));
    1187           0 :         return nullptr;
    1188             :     }
    1189             : 
    1190         164 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
    1191          82 :         !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
    1192             :     {
    1193           0 :         return nullptr;
    1194             :     }
    1195             : 
    1196          82 :     if (dims[iYDim].type() != TILEDB_UINT64)
    1197             :     {
    1198           0 :         const char *pszTypeName = "";
    1199           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1200           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1201             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1202           0 :         return nullptr;
    1203             :     }
    1204          82 :     if (!pszYSize)
    1205             :     {
    1206          15 :         const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
    1207          15 :         const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
    1208          30 :         if (nStart != 0 ||
    1209          15 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1210             :         {
    1211           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1212             :                      "Invalid bounds for Y dimension.");
    1213           0 :             return nullptr;
    1214             :         }
    1215          15 :         poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
    1216             :     }
    1217          82 :     const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
    1218          82 :     if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1219             :     {
    1220           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
    1221           0 :         return nullptr;
    1222             :     }
    1223          82 :     poDS->nBlockYSize = static_cast<int>(nBlockYSize);
    1224             : 
    1225          82 :     if (dims[iXDim].type() != TILEDB_UINT64)
    1226             :     {
    1227           0 :         const char *pszTypeName = "";
    1228           0 :         tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
    1229           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1230             :                  "Unsupported Y dimension type: %s", pszTypeName);
    1231           0 :         return nullptr;
    1232             :     }
    1233          82 :     if (!pszXSize)
    1234             :     {
    1235          15 :         const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
    1236          15 :         const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
    1237          30 :         if (nStart != 0 ||
    1238          15 :             nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
    1239             :         {
    1240           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1241             :                      "Invalid bounds for X dimension.");
    1242           0 :             return nullptr;
    1243             :         }
    1244          15 :         poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
    1245             :     }
    1246          82 :     const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
    1247          82 :     if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
    1248             :     {
    1249           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
    1250           0 :         return nullptr;
    1251             :     }
    1252          82 :     poDS->nBlockXSize = static_cast<int>(nBlockXSize);
    1253             : 
    1254          82 :     poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
    1255          82 :     poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
    1256             : 
    1257          82 :     if (dims.size() == 3)
    1258             :     {
    1259             :         // Create band information objects.
    1260         718 :         for (int i = 1; i <= poDS->nBands; ++i)
    1261             :         {
    1262         640 :             if (pszAttr == nullptr)
    1263         108 :                 poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
    1264             :             else
    1265        1064 :                 poDS->SetBand(
    1266        1064 :                     i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
    1267             :         }
    1268             :     }
    1269             :     else  // subdatasets or only attributes
    1270             :     {
    1271           5 :         if ((poOpenInfo->eAccess == GA_Update) &&
    1272           1 :             (poDS->GetMetadata("SUBDATASETS") != nullptr))
    1273             :         {
    1274           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1275             :                      "The TileDB driver does not support update access "
    1276             :                      "to subdatasets.");
    1277           0 :             return nullptr;
    1278             :         }
    1279             : 
    1280           4 :         if (!osSubdataset.empty())
    1281             :         {
    1282             :             // do we have the attribute in the schema
    1283           2 :             if (schema.attributes().count(osSubdataset))
    1284             :             {
    1285           0 :                 poDS->SetBand(
    1286           0 :                     1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
    1287             :             }
    1288             :             else
    1289             :             {
    1290           2 :                 if (schema.attributes().count(osSubdataset + "_1"))
    1291             :                 {
    1292             :                     // Create band information objects.
    1293           2 :                     for (int i = 1; i <= poDS->nBands; ++i)
    1294             :                     {
    1295           1 :                         CPLString osAttr = CPLString().Printf(
    1296           2 :                             "%s_%d", osSubdataset.c_str(), i);
    1297           2 :                         poDS->SetBand(
    1298           1 :                             i, new TileDBRasterBand(poDS.get(), i, osAttr));
    1299             :                     }
    1300             :                 }
    1301             :                 else
    1302             :                 {
    1303           1 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1304             :                              "%s attribute is not found in TileDB schema.",
    1305             :                              osSubdataset.c_str());
    1306           1 :                     return nullptr;
    1307             :                 }
    1308             :             }
    1309             :         }
    1310             :         else
    1311             :         {
    1312           2 :             char **papszMeta = poDS->GetMetadata("SUBDATASETS");
    1313           2 :             if (papszMeta != nullptr)
    1314             :             {
    1315           0 :                 if ((CSLCount(papszMeta) / 2) == 1)
    1316             :                 {
    1317             :                     CPLString osDSName = CSLFetchNameValueDef(
    1318           0 :                         poDS->papszSubDatasets, "SUBDATASET_1_NAME", "");
    1319           0 :                     return (GDALDataset *)GDALOpen(osDSName,
    1320           0 :                                                    poOpenInfo->eAccess);
    1321             :                 }
    1322             :             }
    1323           2 :             else if (poDS->eIndexMode == ATTRIBUTES)
    1324             :             {
    1325           2 :                 poDS->nBands = schema.attribute_num();
    1326             : 
    1327             :                 // Create band information objects.
    1328           8 :                 for (int i = 1; i <= poDS->nBands; ++i)
    1329             :                 {
    1330             :                     CPLString osAttr =
    1331          18 :                         TILEDB_VALUES + CPLString().Printf("_%i", i);
    1332          12 :                     poDS->SetBand(i,
    1333           6 :                                   new TileDBRasterBand(poDS.get(), i, osAttr));
    1334             :                 }
    1335             :             }
    1336             :             else
    1337             :             {
    1338           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1339             :                          "%s is missing required TileDB subdataset metadata.",
    1340             :                          osArrayPath.c_str());
    1341           0 :                 return nullptr;
    1342             :             }
    1343             :         }
    1344             :     }
    1345             : 
    1346             :     // reload metadata now that bands are created to populate band metadata
    1347          81 :     poDS->TryLoadCachedXML(nullptr, false);
    1348             : 
    1349         162 :     tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
    1350             : 
    1351          81 :     if (!STARTS_WITH_CI(osArrayPath, "TILEDB:") && vfs.is_dir(osArrayPath))
    1352          81 :         poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
    1353             :     else
    1354           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1355             :                  "Overviews not supported for network writes.");
    1356             : 
    1357          81 :     return poDS.release();
    1358             : }
    1359             : 
    1360             : /************************************************************************/
    1361             : /*                              CreateAttribute()                       */
    1362             : /************************************************************************/
    1363             : 
    1364          99 : CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
    1365             :                                             const CPLString &osAttrName,
    1366             :                                             const int nSubRasterCount)
    1367             : {
    1368             :     try
    1369             :     {
    1370         203 :         for (int i = 0; i < nSubRasterCount; ++i)
    1371             :         {
    1372         104 :             CPLString osName(osAttrName);
    1373             :             // a few special cases
    1374             :             // remove any leading slashes or
    1375             :             // additional slashes as in the case of hdf5
    1376         104 :             if STARTS_WITH (osName, "//")
    1377             :             {
    1378           2 :                 osName = osName.substr(2);
    1379             :             }
    1380             : 
    1381         104 :             osName.replaceAll("/", "_");
    1382         104 :             CPLString osPrettyName = osName;
    1383             : 
    1384         104 :             if ((eIndexMode == ATTRIBUTES) ||
    1385          92 :                 ((bHasSubDatasets) && (nSubRasterCount > 1)))
    1386             :             {
    1387          12 :                 osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
    1388             :             }
    1389             : 
    1390         104 :             switch (eType)
    1391             :             {
    1392          47 :                 case GDT_Byte:
    1393             :                 {
    1394          47 :                     m_schema->add_attribute(
    1395          94 :                         tiledb::Attribute::create<unsigned char>(
    1396          94 :                             *m_ctx, osName, *m_filterList));
    1397          47 :                     nBitsPerSample = 8;
    1398          47 :                     break;
    1399             :                 }
    1400           3 :                 case GDT_Int8:
    1401             :                 {
    1402           9 :                     m_schema->add_attribute(tiledb::Attribute::create<int8_t>(
    1403           6 :                         *m_ctx, osName, *m_filterList));
    1404           3 :                     nBitsPerSample = 8;
    1405           3 :                     break;
    1406             :                 }
    1407           4 :                 case GDT_UInt16:
    1408             :                 {
    1409           4 :                     m_schema->add_attribute(
    1410           8 :                         tiledb::Attribute::create<unsigned short>(
    1411           8 :                             *m_ctx, osName, *m_filterList));
    1412           4 :                     nBitsPerSample = 16;
    1413           4 :                     break;
    1414             :                 }
    1415           4 :                 case GDT_UInt32:
    1416             :                 {
    1417           4 :                     m_schema->add_attribute(
    1418           8 :                         tiledb::Attribute::create<unsigned int>(*m_ctx, osName,
    1419           8 :                                                                 *m_filterList));
    1420           4 :                     nBitsPerSample = 32;
    1421           4 :                     break;
    1422             :                 }
    1423           3 :                 case GDT_UInt64:
    1424             :                 {
    1425           9 :                     m_schema->add_attribute(tiledb::Attribute::create<uint64_t>(
    1426           6 :                         *m_ctx, osName, *m_filterList));
    1427           3 :                     nBitsPerSample = 64;
    1428           3 :                     break;
    1429             :                 }
    1430           4 :                 case GDT_Int16:
    1431             :                 {
    1432          12 :                     m_schema->add_attribute(tiledb::Attribute::create<short>(
    1433           8 :                         *m_ctx, osName, *m_filterList));
    1434           4 :                     nBitsPerSample = 16;
    1435           4 :                     break;
    1436             :                 }
    1437           6 :                 case GDT_Int32:
    1438             :                 {
    1439          18 :                     m_schema->add_attribute(tiledb::Attribute::create<int>(
    1440          12 :                         *m_ctx, osName, *m_filterList));
    1441           6 :                     nBitsPerSample = 32;
    1442           6 :                     break;
    1443             :                 }
    1444           3 :                 case GDT_Int64:
    1445             :                 {
    1446           9 :                     m_schema->add_attribute(tiledb::Attribute::create<int64_t>(
    1447           6 :                         *m_ctx, osName, *m_filterList));
    1448           3 :                     nBitsPerSample = 64;
    1449           3 :                     break;
    1450             :                 }
    1451           8 :                 case GDT_Float32:
    1452             :                 {
    1453          24 :                     m_schema->add_attribute(tiledb::Attribute::create<float>(
    1454          16 :                         *m_ctx, osName, *m_filterList));
    1455           8 :                     nBitsPerSample = 32;
    1456           8 :                     break;
    1457             :                 }
    1458           4 :                 case GDT_Float64:
    1459             :                 {
    1460          12 :                     m_schema->add_attribute(tiledb::Attribute::create<double>(
    1461           8 :                         *m_ctx, osName, *m_filterList));
    1462           4 :                     nBitsPerSample = 64;
    1463           4 :                     break;
    1464             :                 }
    1465           4 :                 case GDT_CInt16:
    1466             :                 {
    1467          12 :                     m_schema->add_attribute(tiledb::Attribute::create<short[2]>(
    1468           8 :                         *m_ctx, osName, *m_filterList));
    1469           4 :                     nBitsPerSample = 16;
    1470           4 :                     break;
    1471             :                 }
    1472           4 :                 case GDT_CInt32:
    1473             :                 {
    1474          12 :                     m_schema->add_attribute(tiledb::Attribute::create<int[2]>(
    1475           8 :                         *m_ctx, osName, *m_filterList));
    1476           4 :                     nBitsPerSample = 32;
    1477           4 :                     break;
    1478             :                 }
    1479           4 :                 case GDT_CFloat32:
    1480             :                 {
    1481          12 :                     m_schema->add_attribute(tiledb::Attribute::create<float[2]>(
    1482           8 :                         *m_ctx, osName, *m_filterList));
    1483           4 :                     nBitsPerSample = 32;
    1484           4 :                     break;
    1485             :                 }
    1486           6 :                 case GDT_CFloat64:
    1487             :                 {
    1488           6 :                     m_schema->add_attribute(
    1489          12 :                         tiledb::Attribute::create<double[2]>(*m_ctx, osName,
    1490          12 :                                                              *m_filterList));
    1491           6 :                     nBitsPerSample = 64;
    1492           6 :                     break;
    1493             :                 }
    1494           0 :                 default:
    1495           0 :                     return CE_Failure;
    1496             :             }
    1497             : 
    1498         104 :             if ((bHasSubDatasets) && (i == 0))
    1499             :             {
    1500           6 :                 ++nSubDataCount;
    1501             : 
    1502           6 :                 CPLString osDim;
    1503           6 :                 switch (nSubRasterCount)
    1504             :                 {
    1505           0 :                     case 2:
    1506           0 :                         osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
    1507           0 :                         break;
    1508           6 :                     default:
    1509             :                         osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
    1510           6 :                                      nRasterYSize);
    1511           6 :                         break;
    1512             :                 }
    1513             : 
    1514           6 :                 papszSubDatasets = CSLSetNameValue(
    1515             :                     papszSubDatasets,
    1516          12 :                     CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
    1517          12 :                     CPLString().Printf("%s", osPrettyName.c_str()));
    1518             : 
    1519           6 :                 papszSubDatasets = CSLSetNameValue(
    1520             :                     papszSubDatasets,
    1521          12 :                     CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
    1522          12 :                     CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
    1523             :                                        osPrettyName.c_str(),
    1524           6 :                                        GDALGetDataTypeName(eType)));
    1525             : 
    1526             :                 // add to PAM metadata
    1527           6 :                 if (psSubDatasetsTree == nullptr)
    1528             :                 {
    1529           3 :                     psSubDatasetsTree =
    1530           3 :                         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset");
    1531             :                 }
    1532             : 
    1533           6 :                 CPLXMLNode *psSubNode = CPLCreateXMLNode(
    1534             :                     psSubDatasetsTree, CXT_Element, "Subdataset");
    1535           6 :                 CPLAddXMLAttributeAndValue(psSubNode, "name",
    1536             :                                            osPrettyName.c_str());
    1537             : 
    1538           6 :                 CPLXMLNode *psMetaNode = CPLCreateXMLNode(
    1539             :                     CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
    1540             :                     CXT_Element, "Metadata");
    1541           6 :                 CPLAddXMLAttributeAndValue(psMetaNode, "domain",
    1542             :                                            "IMAGE_STRUCTURE");
    1543             : 
    1544           6 :                 CPLAddXMLAttributeAndValue(
    1545             :                     CPLCreateXMLElementAndValue(
    1546             :                         psMetaNode, "MDI",
    1547          12 :                         CPLString().Printf("%d", nRasterXSize)),
    1548             :                     "KEY", "X_SIZE");
    1549             : 
    1550           6 :                 CPLAddXMLAttributeAndValue(
    1551             :                     CPLCreateXMLElementAndValue(
    1552             :                         psMetaNode, "MDI",
    1553          12 :                         CPLString().Printf("%d", nRasterYSize)),
    1554             :                     "KEY", "Y_SIZE");
    1555             : 
    1556           6 :                 CPLAddXMLAttributeAndValue(
    1557             :                     CPLCreateXMLElementAndValue(
    1558             :                         psMetaNode, "MDI",
    1559          12 :                         CPLString().Printf("%s", GDALGetDataTypeName(eType))),
    1560             :                     "KEY", "DATA_TYPE");
    1561             : 
    1562           6 :                 if (lpoAttributeDS.size() > 0)
    1563             :                 {
    1564           4 :                     CPLAddXMLAttributeAndValue(
    1565             :                         CPLCreateXMLElementAndValue(
    1566             :                             psMetaNode, "MDI",
    1567           8 :                             CPLString().Printf("%d", nBands)),
    1568             :                         "KEY", "NUM_BANDS");
    1569             :                 }
    1570             :                 else
    1571             :                 {
    1572           2 :                     CPLAddXMLAttributeAndValue(
    1573             :                         CPLCreateXMLElementAndValue(
    1574             :                             psMetaNode, "MDI",
    1575           4 :                             CPLString().Printf("%d", nSubRasterCount)),
    1576             :                         "KEY", "NUM_BANDS");
    1577             :                 }
    1578             : 
    1579           6 :                 CPLAddXMLAttributeAndValue(
    1580             :                     CPLCreateXMLElementAndValue(
    1581             :                         psMetaNode, "MDI",
    1582          12 :                         CPLString().Printf("%d", nBitsPerSample)),
    1583             :                     "KEY", "NBITS");
    1584             :             }
    1585             :         }
    1586          99 :         return CE_None;
    1587             :     }
    1588           0 :     catch (const tiledb::TileDBError &e)
    1589             :     {
    1590           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1591           0 :         return CE_Failure;
    1592             :     }
    1593             : }
    1594             : 
    1595             : /************************************************************************/
    1596             : /*                              SetBlockSize()                          */
    1597             : /************************************************************************/
    1598             : 
    1599           1 : void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
    1600             :                                        char **&papszOptions)
    1601             : 
    1602             : {
    1603           1 :     int nX = 0;
    1604           1 :     int nY = 0;
    1605           1 :     poBand->GetBlockSize(&nX, &nY);
    1606             : 
    1607           1 :     if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") == nullptr)
    1608             :     {
    1609           1 :         papszOptions = CSLSetNameValue(papszOptions, "BLOCKXSIZE",
    1610           2 :                                        CPLString().Printf("%d", nX));
    1611             :     }
    1612             : 
    1613           1 :     if (CSLFetchNameValue(papszOptions, "BLOCKYSIZE") == nullptr)
    1614             :     {
    1615           1 :         papszOptions = CSLSetNameValue(papszOptions, "BLOCKYSIZE",
    1616           2 :                                        CPLString().Printf("%d", nY));
    1617             :     }
    1618           1 : }
    1619             : 
    1620             : /************************************************************************/
    1621             : /*                              CreateLL()                              */
    1622             : /*                                                                      */
    1623             : /*      Shared functionality between TileDBDataset::Create() and        */
    1624             : /*      TileDBDataset::CreateCopy() for creating TileDB array based on  */
    1625             : /*      a set of options and a configuration.                           */
    1626             : /************************************************************************/
    1627             : 
    1628          93 : TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
    1629             :                                                    int nXSize, int nYSize,
    1630             :                                                    int nBandsIn,
    1631             :                                                    GDALDataType eType,
    1632             :                                                    char **papszOptions)
    1633             : {
    1634             :     try
    1635             :     {
    1636          93 :         if ((nXSize <= 0 && nYSize <= 0))
    1637             :         {
    1638           0 :             return nullptr;
    1639             :         }
    1640             : 
    1641         186 :         auto poDS = std::make_unique<TileDBRasterDataset>();
    1642          93 :         poDS->nRasterXSize = nXSize;
    1643          93 :         poDS->nRasterYSize = nYSize;
    1644          93 :         poDS->eDataType = eType;
    1645          93 :         poDS->nBands = nBandsIn;
    1646          93 :         poDS->eAccess = GA_Update;
    1647             : 
    1648          93 :         if (poDS->nBands == 0)  // subdatasets
    1649             :         {
    1650           1 :             poDS->eIndexMode = ATTRIBUTES;
    1651             :         }
    1652             :         else
    1653             :         {
    1654             :             const char *pszIndexMode =
    1655          92 :                 CSLFetchNameValue(papszOptions, "INTERLEAVE");
    1656             : 
    1657          92 :             if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
    1658           0 :                 return nullptr;
    1659             :         }
    1660             : 
    1661             :         const char *pszConfig =
    1662          93 :             CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
    1663          93 :         if (pszConfig != nullptr)
    1664             :         {
    1665           0 :             tiledb::Config cfg(pszConfig);
    1666           0 :             poDS->m_ctx.reset(new tiledb::Context(cfg));
    1667             :         }
    1668             :         else
    1669             :         {
    1670          93 :             poDS->m_ctx.reset(new tiledb::Context());
    1671             :         }
    1672             : 
    1673             :         const char *pszCompression =
    1674          93 :             CSLFetchNameValue(papszOptions, "COMPRESSION");
    1675             :         const char *pszCompressionLevel =
    1676          93 :             CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
    1677             : 
    1678             :         const char *pszBlockXSize =
    1679          93 :             CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
    1680          93 :         poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
    1681             :         const char *pszBlockYSize =
    1682          93 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
    1683          93 :         poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
    1684          93 :         poDS->bStats = CSLFetchBoolean(papszOptions, "STATS", FALSE);
    1685             : 
    1686             :         const char *pszTimestamp =
    1687          93 :             CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
    1688          93 :         if (pszTimestamp != nullptr)
    1689           2 :             poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
    1690             : 
    1691             :         // set dimensions and attribute type for schema
    1692         186 :         poDS->m_schema.reset(
    1693          93 :             new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
    1694          93 :         poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
    1695          93 :         poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
    1696             : 
    1697          93 :         poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
    1698             : 
    1699          93 :         if (pszCompression != nullptr)
    1700             :         {
    1701           0 :             int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
    1702           0 :             if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
    1703           0 :                                          *(poDS->m_filterList.get()),
    1704           0 :                                          pszCompression, nLevel) == CE_None)
    1705             :             {
    1706           0 :                 poDS->SetMetadataItem("COMPRESSION", pszCompression,
    1707           0 :                                       "IMAGE_STRUCTURE");
    1708           0 :                 poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
    1709             :             }
    1710             :         }
    1711             : 
    1712         186 :         CPLString osAux;
    1713          93 :         const char *pszArrayName = CPLGetBasename(pszFilename);
    1714          93 :         osAux.Printf("%s.tdb", pszArrayName);
    1715             : 
    1716          93 :         poDS->SetPhysicalFilename(
    1717             :             CPLFormFilename(pszFilename, osAux.c_str(), nullptr));
    1718             : 
    1719             :         // Initialize PAM information.
    1720          93 :         poDS->SetDescription(pszFilename);
    1721             : 
    1722             :         // this driver enforces that all subdatasets are the same size
    1723         186 :         tiledb::Domain domain(*poDS->m_ctx);
    1724             : 
    1725             :         // Note the dimension bounds are inclusive and are expanded to the match
    1726             :         // the block size
    1727          93 :         poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
    1728          93 :         poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
    1729             : 
    1730          93 :         uint64_t w = (uint64_t)poDS->nBlocksX * poDS->nBlockXSize - 1;
    1731          93 :         uint64_t h = (uint64_t)poDS->nBlocksY * poDS->nBlockYSize - 1;
    1732             : 
    1733             :         auto d1 = tiledb::Dimension::create<uint64_t>(
    1734         279 :             *poDS->m_ctx, "X", {0, w}, uint64_t(poDS->nBlockXSize));
    1735             :         auto d2 = tiledb::Dimension::create<uint64_t>(
    1736         279 :             *poDS->m_ctx, "Y", {0, h}, uint64_t(poDS->nBlockYSize));
    1737             : 
    1738             :         {
    1739             :             // Only used for unit test purposes (to check ability of GDAL to read
    1740             :             // an arbitrary array)
    1741             :             const char *pszAttrName =
    1742          93 :                 CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    1743          93 :             if ((poDS->nBands == 0) || (poDS->eIndexMode == ATTRIBUTES))
    1744             :             {
    1745           5 :                 poDS->AddDimensions(domain, pszAttrName, d2, d1, nullptr);
    1746             :             }
    1747             :             else
    1748             :             {
    1749             :                 auto d3 = tiledb::Dimension::create<uint64_t>(
    1750         264 :                     *poDS->m_ctx, "BANDS", {1, uint64_t(poDS->nBands)}, 1);
    1751          88 :                 poDS->AddDimensions(domain, pszAttrName, d2, d1, &d3);
    1752             :             }
    1753             :         }
    1754             : 
    1755          93 :         poDS->m_schema->set_domain(domain).set_order(
    1756          93 :             {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
    1757             :         ;
    1758             : 
    1759             :         // register additional attributes to the pixel value, these will be
    1760             :         // be reported as subdatasets on future reads
    1761         186 :         poDS->papszAttributes =
    1762          93 :             CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
    1763             : 
    1764         103 :         for (int i = 0; poDS->papszAttributes != nullptr &&
    1765           6 :                         poDS->papszAttributes[i] != nullptr;
    1766             :              i++)
    1767             :         {
    1768             :             // modeling additional attributes as subdatasets
    1769           4 :             poDS->bHasSubDatasets = true;
    1770             :             // check each attribute is a GDAL source
    1771             :             std::unique_ptr<GDALDataset> poAttrDS(
    1772           8 :                 GDALDataset::Open(poDS->papszAttributes[i], GA_ReadOnly));
    1773             : 
    1774           4 :             if (poAttrDS != nullptr)
    1775             :             {
    1776             :                 const char *pszAttrName =
    1777           4 :                     CPLGetBasename(poAttrDS->GetDescription());
    1778             :                 // check each is co-registered
    1779             :                 // candidate band
    1780           4 :                 int nAttrBands = poAttrDS->GetRasterCount();
    1781           4 :                 if (nAttrBands > 0)
    1782             :                 {
    1783           4 :                     GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
    1784             : 
    1785           4 :                     if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
    1786           8 :                         (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
    1787           4 :                         (poDS->nBands == nAttrBands))
    1788             :                     {
    1789             :                         // could check geotransform, but it is sufficient
    1790             :                         // that cartesian dimensions are equal
    1791           4 :                         poDS->lpoAttributeDS.push_back(std::move(poAttrDS));
    1792           4 :                         poDS->CreateAttribute(poAttrBand->GetRasterDataType(),
    1793             :                                               pszAttrName, 1);
    1794             :                     }
    1795             :                     else
    1796             :                     {
    1797           0 :                         CPLError(
    1798             :                             CE_Warning, CPLE_AppDefined,
    1799             :                             "Skipping %s as it has a different dimension\n",
    1800           0 :                             poDS->papszAttributes[i]);
    1801             :                     }
    1802             :                 }
    1803             :                 else
    1804             :                 {
    1805           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1806             :                              "Skipping %s as it doesn't have any bands\n",
    1807           0 :                              poDS->papszAttributes[i]);
    1808             :                 }
    1809             :             }
    1810             :             else
    1811             :             {
    1812           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1813             :                          "Skipping %s, not recognized as a GDAL dataset\n",
    1814           0 :                          poDS->papszAttributes[i]);
    1815             :             }
    1816             :         }
    1817             : 
    1818          93 :         return poDS.release();
    1819             :     }
    1820           0 :     catch (const tiledb::TileDBError &e)
    1821             :     {
    1822           0 :         CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
    1823           0 :         return nullptr;
    1824             :     }
    1825             : }
    1826             : 
    1827             : /************************************************************************/
    1828             : /*                              CopySubDatasets()                       */
    1829             : /*                                                                      */
    1830             : /*      Copy SubDatasets from src to a TileDBRasterDataset              */
    1831             : /*                                                                      */
    1832             : /************************************************************************/
    1833             : 
    1834           1 : CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
    1835             :                                             TileDBRasterDataset *poDstDS,
    1836             :                                             GDALProgressFunc pfnProgress,
    1837             :                                             void *pProgressData)
    1838             : 
    1839             : {
    1840             :     try
    1841             :     {
    1842           2 :         std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
    1843           1 :         poDstDS->bHasSubDatasets = true;
    1844           1 :         char **papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
    1845           1 :         if (!papszSrcSubDatasets)
    1846           0 :             return CE_Failure;
    1847             :         const char *pszSubDSName =
    1848           1 :             CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    1849           1 :         if (!pszSubDSName)
    1850           0 :             return CE_Failure;
    1851             : 
    1852             :         CPLStringList apszTokens(CSLTokenizeString2(
    1853           2 :             pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
    1854             :         // FIXME? this is tailored for HDF5-like subdataset names
    1855             :         // HDF5:foo.hdf5:attrname.
    1856           1 :         if (apszTokens.size() != 3)
    1857             :         {
    1858           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1859             :                      "Cannot guess attribute name in %s", pszSubDSName);
    1860           0 :             return CE_Failure;
    1861             :         }
    1862             : 
    1863             :         std::unique_ptr<GDALDataset> poSubDataset(
    1864           2 :             GDALDataset::Open(pszSubDSName));
    1865           2 :         if (poSubDataset.get() == nullptr ||
    1866           1 :             poSubDataset->GetRasterCount() == 0)
    1867             :         {
    1868           0 :             return CE_Failure;
    1869             :         }
    1870             : 
    1871           1 :         uint64_t nSubXSize = poSubDataset->GetRasterXSize();
    1872           1 :         uint64_t nSubYSize = poSubDataset->GetRasterYSize();
    1873             : 
    1874           1 :         const char *pszAttrName = apszTokens[2];
    1875             : 
    1876           1 :         poDstDS->CreateAttribute(
    1877             :             poSubDataset->GetRasterBand(1)->GetRasterDataType(), pszAttrName,
    1878             :             poSubDataset->GetRasterCount());
    1879           1 :         apoDatasets.push_back(std::move(poSubDataset));
    1880             : 
    1881           5 :         for (int i = 0; papszSrcSubDatasets[i] != nullptr; i++)
    1882             :         {
    1883           4 :             if (STARTS_WITH_CI(papszSrcSubDatasets[i], "SUBDATASET_1_NAME=") ||
    1884           3 :                 strstr(papszSrcSubDatasets[i], "_DESC="))
    1885             :             {
    1886           3 :                 continue;
    1887             :             }
    1888           1 :             pszSubDSName = CPLParseNameValue(papszSrcSubDatasets[i], nullptr);
    1889             :             apszTokens = CSLTokenizeString2(
    1890           1 :                 pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
    1891           1 :             if (apszTokens.size() != 3)
    1892             :             {
    1893           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1894             :                          "Cannot guess attribute name in %s", pszSubDSName);
    1895           0 :                 continue;
    1896             :             }
    1897             : 
    1898             :             std::unique_ptr<GDALDataset> poSubDS(
    1899           2 :                 GDALDataset::Open(pszSubDSName));
    1900           1 :             if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
    1901             :             {
    1902           1 :                 GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
    1903             :                 int nBlockXSize, nBlockYSize;
    1904           1 :                 poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1905             : 
    1906           1 :                 if ((poSubDS->GetRasterXSize() != (int)nSubXSize) ||
    1907           1 :                     (poSubDS->GetRasterYSize() != (int)nSubYSize) ||
    1908           3 :                     (nBlockXSize != poDstDS->nBlockXSize) ||
    1909           1 :                     (nBlockYSize != poDstDS->nBlockYSize))
    1910             :                 {
    1911           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1912             :                              "Sub-datasets must have the same dimension,"
    1913             :                              " and block sizes, skipping %s\n",
    1914             :                              pszSubDSName);
    1915             :                 }
    1916             :                 else
    1917             :                 {
    1918           1 :                     pszAttrName = apszTokens[2];
    1919           1 :                     poDstDS->CreateAttribute(
    1920             :                         poSubDS->GetRasterBand(1)->GetRasterDataType(),
    1921             :                         pszAttrName, poSubDS->GetRasterCount());
    1922           1 :                     apoDatasets.push_back(std::move(poSubDS));
    1923             :                 }
    1924             :             }
    1925             :             else
    1926             :             {
    1927           0 :                 CPLError(
    1928             :                     CE_Warning, CPLE_AppDefined,
    1929             :                     "Sub-datasets must be not null and contain data in bands,"
    1930             :                     "skipping %s\n",
    1931             :                     pszSubDSName);
    1932             :             }
    1933             :         }
    1934             : 
    1935           1 :         poDstDS->SetMetadata(poDstDS->papszSubDatasets, "SUBDATASETS");
    1936           1 :         tiledb::Array::create(poDstDS->GetDescription(), *poDstDS->m_schema);
    1937             : 
    1938           1 :         if (poDstDS->nTimestamp)
    1939             :         {
    1940           0 :             poDstDS->m_array.reset(new tiledb::Array(
    1941           0 :                 *poDstDS->m_ctx, poDstDS->GetDescription(), TILEDB_WRITE,
    1942           0 :                 tiledb::TemporalPolicy(tiledb::TimeTravel,
    1943           0 :                                        poDstDS->nTimestamp)));
    1944             :         }
    1945             :         else
    1946           3 :             poDstDS->m_array.reset(new tiledb::Array(
    1947           3 :                 *poDstDS->m_ctx, poDstDS->GetDescription(), TILEDB_WRITE));
    1948             : 
    1949             :         /* --------------------------------------------------------  */
    1950             :         /*      Report preliminary (0) progress.                     */
    1951             :         /* --------------------------------------------------------- */
    1952           1 :         if (!pfnProgress(0.0, nullptr, pProgressData))
    1953             :         {
    1954           0 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    1955             :                      "User terminated CreateCopy()");
    1956           0 :             return CE_Failure;
    1957             :         }
    1958             : 
    1959             :         // copy over subdatasets by block
    1960           2 :         tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
    1961           1 :         query.set_layout(TILEDB_GLOBAL_ORDER);
    1962           1 :         int nTotalBlocks = poDstDS->nBlocksX * poDstDS->nBlocksY;
    1963             : 
    1964             :         // row-major
    1965           6 :         for (int j = 0; j < poDstDS->nBlocksY; ++j)
    1966             :         {
    1967          55 :             for (int i = 0; i < poDstDS->nBlocksX; ++i)
    1968             :             {
    1969          50 :                 std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
    1970             :                 // have to write set all tiledb attributes on write
    1971          50 :                 int iAttr = 0;
    1972         150 :                 for (auto &poSubDS : apoDatasets)
    1973             :                 {
    1974             :                     GDALDataType eDT =
    1975         100 :                         poSubDS->GetRasterBand(1)->GetRasterDataType();
    1976             : 
    1977         200 :                     for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
    1978             :                     {
    1979         100 :                         int nBytes = GDALGetDataTypeSizeBytes(eDT);
    1980         100 :                         int nValues = nBytes * poDstDS->nBlockXSize *
    1981         100 :                                       poDstDS->nBlockYSize;
    1982         100 :                         void *pBlock = VSIMalloc(nBytes * nValues);
    1983         100 :                         aBlocks.emplace_back(pBlock, &VSIFree);
    1984         100 :                         GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
    1985         100 :                         if (poBand->ReadBlock(i, j, pBlock) == CE_None)
    1986             :                         {
    1987         100 :                             SetBuffer(
    1988             :                                 &query, eDT,
    1989         200 :                                 poDstDS->m_schema->attribute(iAttr++).name(),
    1990             :                                 pBlock,
    1991         100 :                                 poDstDS->nBlockXSize * poDstDS->nBlockYSize);
    1992             :                         }
    1993             :                     }
    1994             :                 }
    1995             : 
    1996          50 :                 if (poDstDS->bStats)
    1997           0 :                     tiledb::Stats::enable();
    1998             : 
    1999          50 :                 auto status = query.submit();
    2000             : 
    2001          50 :                 if (poDstDS->bStats)
    2002             :                 {
    2003           0 :                     tiledb::Stats::dump(stdout);
    2004           0 :                     tiledb::Stats::disable();
    2005             :                 }
    2006             : 
    2007          50 :                 if (status == tiledb::Query::Status::FAILED)
    2008             :                 {
    2009           0 :                     return CE_Failure;
    2010             :                 }
    2011             : 
    2012          50 :                 int nBlocks = ((j + 1) * poDstDS->nBlocksX);
    2013             : 
    2014          50 :                 if (!pfnProgress(nBlocks / static_cast<double>(nTotalBlocks),
    2015             :                                  nullptr, pProgressData))
    2016             :                 {
    2017           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt,
    2018             :                              "User terminated CreateCopy()");
    2019           0 :                     return CE_Failure;
    2020             :                 }
    2021             :             }
    2022             :         }
    2023             : 
    2024           1 :         query.finalize();
    2025             : 
    2026           1 :         return CE_None;
    2027             :     }
    2028           0 :     catch (const tiledb::TileDBError &e)
    2029             :     {
    2030           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2031           0 :         return CE_Failure;
    2032             :     }
    2033             : }
    2034             : 
    2035             : /************************************************************************/
    2036             : /*                              Create()                                */
    2037             : /************************************************************************/
    2038             : 
    2039          92 : GDALDataset *TileDBRasterDataset::Create(const char *pszFilename, int nXSize,
    2040             :                                          int nYSize, int nBandsIn,
    2041             :                                          GDALDataType eType,
    2042             :                                          char **papszOptions)
    2043             : 
    2044             : {
    2045         184 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2046             : 
    2047             :     std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
    2048         184 :         osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
    2049             : 
    2050          92 :     if (!poDS)
    2051           0 :         return nullptr;
    2052             : 
    2053          92 :     tiledb::Array::create(osArrayPath, *poDS->m_schema);
    2054             : 
    2055          79 :     if (poDS->nTimestamp)
    2056           6 :         poDS->m_array.reset(new tiledb::Array(
    2057           2 :             *poDS->m_ctx, osArrayPath, TILEDB_WRITE,
    2058           6 :             tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp)));
    2059             :     else
    2060         231 :         poDS->m_array.reset(
    2061         154 :             new tiledb::Array(*poDS->m_ctx, osArrayPath, TILEDB_WRITE));
    2062             : 
    2063             :     const char *pszAttrName =
    2064          79 :         CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
    2065         224 :     for (int i = 0; i < poDS->nBands; i++)
    2066             :     {
    2067         145 :         if (poDS->eIndexMode == ATTRIBUTES)
    2068          20 :             poDS->SetBand(
    2069             :                 i + 1, new TileDBRasterBand(
    2070          10 :                            poDS.get(), i + 1,
    2071          20 :                            TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
    2072             :         else
    2073         405 :             poDS->SetBand(i + 1,
    2074         270 :                           new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
    2075             :     }
    2076             : 
    2077             :     // Only used for unit test purposes (to check ability of GDAL to read
    2078             :     // an arbitrary array)
    2079          79 :     if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
    2080             :     {
    2081          65 :         char **papszImageStruct = nullptr;
    2082             :         papszImageStruct =
    2083          65 :             CSLAddNameValue(papszImageStruct, "NBITS",
    2084         130 :                             CPLString().Printf("%d", poDS->nBitsPerSample));
    2085          65 :         papszImageStruct = CSLAddNameValue(
    2086             :             papszImageStruct, "DATA_TYPE",
    2087         130 :             CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
    2088             :         papszImageStruct =
    2089          65 :             CSLAddNameValue(papszImageStruct, "X_SIZE",
    2090         130 :                             CPLString().Printf("%d", poDS->nRasterXSize));
    2091             :         papszImageStruct =
    2092          65 :             CSLAddNameValue(papszImageStruct, "Y_SIZE",
    2093         130 :                             CPLString().Printf("%d", poDS->nRasterYSize));
    2094          65 :         papszImageStruct = CSLAddNameValue(papszImageStruct, "INTERLEAVE",
    2095          65 :                                            index_type_name(poDS->eIndexMode));
    2096          65 :         papszImageStruct = CSLAddNameValue(papszImageStruct, "DATASET_TYPE",
    2097             :                                            RASTER_DATASET_TYPE);
    2098             : 
    2099          65 :         if (poDS->lpoAttributeDS.size() > 0)
    2100             :         {
    2101           2 :             int i = 0;
    2102           6 :             for (auto const &poAttrDS : poDS->lpoAttributeDS)
    2103             :             {
    2104           8 :                 papszImageStruct = CSLAddNameValue(
    2105             :                     papszImageStruct,
    2106           8 :                     CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
    2107           4 :                     CPLGetBasename(poAttrDS->GetDescription()));
    2108             :             }
    2109             :         }
    2110          65 :         poDS->SetMetadata(papszImageStruct, "IMAGE_STRUCTURE");
    2111             : 
    2112          65 :         CSLDestroy(papszImageStruct);
    2113             :     }
    2114             : 
    2115          79 :     return poDS.release();
    2116             : }
    2117             : 
    2118             : /************************************************************************/
    2119             : /*                              CreateCopy()                            */
    2120             : /************************************************************************/
    2121             : 
    2122          58 : GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
    2123             :                                              GDALDataset *poSrcDS, int bStrict,
    2124             :                                              char **papszOptions,
    2125             :                                              GDALProgressFunc pfnProgress,
    2126             :                                              void *pProgressData)
    2127             : 
    2128             : {
    2129          58 :     char **papszCopyOptions = CSLDuplicate(papszOptions);
    2130         116 :     CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
    2131             : 
    2132          58 :     std::unique_ptr<TileDBRasterDataset> poDstDS;
    2133             : 
    2134          58 :     if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
    2135             :     {
    2136             :         // TileDB schemas are fixed
    2137           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2138             :                  "TileDB driver does not support "
    2139             :                  "appending to an existing schema.");
    2140           0 :         CSLDestroy(papszCopyOptions);
    2141           0 :         return nullptr;
    2142             :     }
    2143             : 
    2144          58 :     char **papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
    2145             : 
    2146          58 :     if (papszSrcSubDatasets == nullptr)
    2147             :     {
    2148          57 :         const int nBands = poSrcDS->GetRasterCount();
    2149             : 
    2150          57 :         if (nBands > 0)
    2151             :         {
    2152          57 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
    2153          57 :             GDALDataType eType = poBand->GetRasterDataType();
    2154             : 
    2155          87 :             for (int i = 2; i <= nBands; ++i)
    2156             :             {
    2157          30 :                 if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
    2158             :                 {
    2159           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2160             :                              "TileDB driver does not support "
    2161             :                              "source dataset with different band data types.");
    2162           0 :                     CSLDestroy(papszCopyOptions);
    2163           0 :                     return nullptr;
    2164             :                 }
    2165             :             }
    2166             : 
    2167          44 :             poDstDS.reset(
    2168          57 :                 static_cast<TileDBRasterDataset *>(TileDBRasterDataset::Create(
    2169             :                     osArrayPath, poSrcDS->GetRasterXSize(),
    2170             :                     poSrcDS->GetRasterYSize(), nBands, eType, papszOptions)));
    2171             : 
    2172          44 :             if (!poDstDS)
    2173             :             {
    2174           0 :                 CSLDestroy(papszCopyOptions);
    2175           0 :                 return nullptr;
    2176             :             }
    2177             : 
    2178             :             CPLErr eErr =
    2179          44 :                 GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
    2180             :                                            pfnProgress, pProgressData);
    2181             : 
    2182          44 :             if (eErr != CE_None)
    2183             :             {
    2184           0 :                 CPLError(eErr, CPLE_AppDefined,
    2185             :                          "Error copying raster to TileDB.");
    2186             :             }
    2187             :         }
    2188             :         else
    2189             :         {
    2190           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2191             :                      "TileDB driver does not support "
    2192             :                      "source dataset with zero bands.");
    2193             :         }
    2194             :     }
    2195             :     else
    2196             :     {
    2197           1 :         if (bStrict)
    2198             :         {
    2199           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2200             :                      "TileDB driver does not support copying "
    2201             :                      "subdatasets in strict mode.");
    2202           0 :             CSLDestroy(papszCopyOptions);
    2203           0 :             return nullptr;
    2204             :         }
    2205             : 
    2206           2 :         if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
    2207           1 :             CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
    2208             :         {
    2209           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2210             :                      "Changing block size is not supported when copying "
    2211             :                      "subdatasets.");
    2212           0 :             CSLDestroy(papszCopyOptions);
    2213           0 :             return nullptr;
    2214             :         }
    2215             : 
    2216           1 :         const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
    2217             :         const int nMaxFiles =
    2218           1 :             atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
    2219             : 
    2220           1 :         if (nSubDatasetCount <= nMaxFiles)
    2221             :         {
    2222             :             const char *pszSource =
    2223           1 :                 CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
    2224           1 :             if (pszSource)
    2225             :             {
    2226             :                 std::unique_ptr<GDALDataset> poSubDataset(
    2227           2 :                     GDALDataset::Open(pszSource));
    2228           1 :                 if (poSubDataset && poSubDataset->GetRasterCount() > 0)
    2229             :                 {
    2230           1 :                     GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
    2231             : 
    2232           1 :                     TileDBRasterDataset::SetBlockSize(poBand, papszCopyOptions);
    2233           1 :                     poDstDS.reset(TileDBRasterDataset::CreateLL(
    2234             :                         osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
    2235             :                         poBand->GetRasterDataType(), papszCopyOptions));
    2236             : 
    2237           1 :                     if (poDstDS && TileDBRasterDataset::CopySubDatasets(
    2238             :                                        poSrcDS, poDstDS.get(), pfnProgress,
    2239           1 :                                        pProgressData) != CE_None)
    2240             :                     {
    2241           0 :                         poDstDS.reset();
    2242           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2243             :                                  "Unable to copy subdatasets.");
    2244             :                     }
    2245             :                 }
    2246             :             }
    2247             :         }
    2248             :         else
    2249             :         {
    2250           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2251             :                      "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
    2252             :         }
    2253             :     }
    2254             : 
    2255          45 :     CSLDestroy(papszCopyOptions);
    2256             : 
    2257             :     // TODO Supporting mask bands is a possible future task
    2258          45 :     if (poDstDS != nullptr)
    2259             :     {
    2260          45 :         int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
    2261          45 :         poDstDS->CloneInfo(poSrcDS, nCloneFlags);
    2262             : 
    2263          45 :         if (poDstDS->eIndexMode == ATTRIBUTES)
    2264             :         {
    2265           5 :             poDstDS->FlushCache(false);
    2266             :         }
    2267             : 
    2268          45 :         poDstDS->m_array->close();
    2269          45 :         poDstDS->eAccess = GA_ReadOnly;
    2270          45 :         poDstDS->m_array->open(TILEDB_READ);
    2271             : 
    2272          45 :         return poDstDS.release();
    2273             :     }
    2274           0 :     return nullptr;
    2275             : }

Generated by: LCOV version 1.14