LCOV - code coverage report
Current view: top level - frmts/tiledb - tiledbmultidimarray.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 673 783 86.0 %
Date: 2024-05-13 13:33:37 Functions: 25 28 89.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL TileDB Driver
       4             :  * Purpose:  Implement GDAL TileDB multidimensional support based on https://www.tiledb.io
       5             :  * Author:   TileDB, Inc
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, 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 "tiledbmultidim.h"
      30             : 
      31             : #include <algorithm>
      32             : #include <limits>
      33             : 
      34             : /************************************************************************/
      35             : /*                   TileDBArray::TileDBArray()                         */
      36             : /************************************************************************/
      37             : 
      38          75 : TileDBArray::TileDBArray(
      39             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
      40             :     const std::string &osParentName, const std::string &osName,
      41             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
      42          75 :     const GDALExtendedDataType &oType, const std::string &osPath)
      43             :     : GDALAbstractMDArray(osParentName, osName),
      44             :       GDALMDArray(osParentName, osName), m_poSharedResource(poSharedResource),
      45             :       m_aoDims(aoDims), m_oType(oType), m_osPath(osPath),
      46          75 :       m_bStats(poSharedResource->GetDumpStats())
      47             : {
      48          75 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                   TileDBArray::Create()                              */
      52             : /************************************************************************/
      53             : 
      54          75 : /*static*/ std::shared_ptr<TileDBArray> TileDBArray::Create(
      55             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
      56             :     const std::string &osParentName, const std::string &osName,
      57             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
      58             :     const GDALExtendedDataType &oType, const std::string &osPath)
      59             : {
      60             :     auto poArray = std::shared_ptr<TileDBArray>(new TileDBArray(
      61          75 :         poSharedResource, osParentName, osName, aoDims, oType, osPath));
      62          75 :     poArray->SetSelf(poArray);
      63          75 :     return poArray;
      64             : }
      65             : 
      66             : /************************************************************************/
      67             : /*                   TileDBArray::~TileDBArray()                        */
      68             : /************************************************************************/
      69             : 
      70         150 : TileDBArray::~TileDBArray()
      71             : {
      72          75 :     if (!m_bFinalized)
      73           5 :         Finalize();
      74         150 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                   BuildDimensionLabelName()                          */
      78             : /************************************************************************/
      79             : 
      80             : static std::string
      81          77 : BuildDimensionLabelName(const std::shared_ptr<GDALDimension> &poDim)
      82             : {
      83          77 :     return poDim->GetName() + "_label";
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                   TileDBDataTypeToGDALDataType()                     */
      88             : /************************************************************************/
      89             : 
      90             : /*static*/ GDALDataType
      91          71 : TileDBArray::TileDBDataTypeToGDALDataType(tiledb_datatype_t tiledb_dt)
      92             : {
      93          71 :     GDALDataType eDT = GDT_Unknown;
      94          71 :     switch (tiledb_dt)
      95             :     {
      96           7 :         case TILEDB_UINT8:
      97           7 :             eDT = GDT_Byte;
      98           7 :             break;
      99             : 
     100           1 :         case TILEDB_INT8:
     101           1 :             eDT = GDT_Int8;
     102           1 :             break;
     103             : 
     104           1 :         case TILEDB_UINT16:
     105           1 :             eDT = GDT_UInt16;
     106           1 :             break;
     107             : 
     108           2 :         case TILEDB_INT16:
     109           2 :             eDT = GDT_Int16;
     110           2 :             break;
     111             : 
     112           1 :         case TILEDB_UINT32:
     113           1 :             eDT = GDT_UInt32;
     114           1 :             break;
     115             : 
     116           7 :         case TILEDB_INT32:
     117           7 :             eDT = GDT_Int32;
     118           7 :             break;
     119             : 
     120           1 :         case TILEDB_UINT64:
     121           1 :             eDT = GDT_UInt64;
     122           1 :             break;
     123             : 
     124           1 :         case TILEDB_INT64:
     125           1 :             eDT = GDT_Int64;
     126           1 :             break;
     127             : 
     128          12 :         case TILEDB_FLOAT32:
     129          12 :             eDT = GDT_Float32;
     130          12 :             break;
     131             : 
     132          37 :         case TILEDB_FLOAT64:
     133          37 :             eDT = GDT_Float64;
     134          37 :             break;
     135             : 
     136           1 :         case TILEDB_CHAR:
     137             :         case TILEDB_STRING_ASCII:
     138             :         case TILEDB_STRING_UTF8:
     139             :         case TILEDB_STRING_UTF16:
     140             :         case TILEDB_STRING_UTF32:
     141             :         case TILEDB_STRING_UCS2:
     142             :         case TILEDB_STRING_UCS4:
     143             :         case TILEDB_ANY:
     144             :         case TILEDB_DATETIME_YEAR:
     145             :         case TILEDB_DATETIME_MONTH:
     146             :         case TILEDB_DATETIME_WEEK:
     147             :         case TILEDB_DATETIME_DAY:
     148             :         case TILEDB_DATETIME_HR:
     149             :         case TILEDB_DATETIME_MIN:
     150             :         case TILEDB_DATETIME_SEC:
     151             :         case TILEDB_DATETIME_MS:
     152             :         case TILEDB_DATETIME_US:
     153             :         case TILEDB_DATETIME_NS:
     154             :         case TILEDB_DATETIME_PS:
     155             :         case TILEDB_DATETIME_FS:
     156             :         case TILEDB_DATETIME_AS:
     157             :         case TILEDB_TIME_HR:
     158             :         case TILEDB_TIME_MIN:
     159             :         case TILEDB_TIME_SEC:
     160             :         case TILEDB_TIME_MS:
     161             :         case TILEDB_TIME_US:
     162             :         case TILEDB_TIME_NS:
     163             :         case TILEDB_TIME_PS:
     164             :         case TILEDB_TIME_FS:
     165             :         case TILEDB_TIME_AS:
     166             :         case TILEDB_BLOB:
     167             :         case TILEDB_BOOL:
     168             : #ifdef HAS_TILEDB_GEOM_WKB_WKT
     169             :         case TILEDB_GEOM_WKB:
     170             :         case TILEDB_GEOM_WKT:
     171             : #endif
     172             :         {
     173           1 :             break;
     174             :         }
     175             :     }
     176          71 :     return eDT;
     177             : }
     178             : 
     179             : /************************************************************************/
     180             : /*                   TileDBArray::Finalize()                            */
     181             : /************************************************************************/
     182             : 
     183          30 : bool TileDBArray::Finalize() const
     184             : {
     185          30 :     if (m_bFinalized)
     186           0 :         return m_poTileDBArray != nullptr;
     187             : 
     188          30 :     m_bFinalized = true;
     189             : 
     190          30 :     CPLAssert(m_poSchema);
     191          30 :     CPLAssert(m_poAttr);
     192             : 
     193             :     try
     194             :     {
     195             :         // TODO: set nodata as fill_value
     196             : 
     197          30 :         m_poSchema->add_attribute(*(m_poAttr.get()));
     198             : 
     199          30 :         tiledb::Array::create(m_osPath, *m_poSchema);
     200             : 
     201          30 :         bool bAdded = false;
     202          60 :         auto poGroup = m_poParent.lock();
     203          30 :         if (!poGroup)
     204             :         {
     205             :             // Temporarily instantiate a TileDBGroup to call AddMember() on it
     206          15 :             poGroup = TileDBGroup::OpenFromDisk(
     207           5 :                 m_poSharedResource,
     208          10 :                 /* osParentName = */ std::string(),
     209          10 :                 CPLGetFilename(m_osParentPath.c_str()), m_osParentPath);
     210             :         }
     211          30 :         if (poGroup)
     212             :         {
     213          30 :             bAdded = poGroup->AddMember(m_osPath, m_osName);
     214             :         }
     215          30 :         if (!bAdded)
     216             :         {
     217           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     218             :                      "Could not add array %s as a member of group %s",
     219           0 :                      m_osName.c_str(), m_osParentPath.c_str());
     220             :         }
     221             : 
     222          30 :         auto &ctx = m_poSharedResource->GetCtx();
     223             :         m_poTileDBArray =
     224          30 :             std::make_unique<tiledb::Array>(ctx, m_osPath, TILEDB_READ);
     225          30 :         if (m_nTimestamp > 0)
     226           0 :             m_poTileDBArray->set_open_timestamp_end(m_nTimestamp);
     227             :         m_poSchema =
     228          30 :             std::make_unique<tiledb::ArraySchema>(m_poTileDBArray->schema());
     229          30 :         m_poAttr.reset();
     230             : 
     231             :         // Write dimension label values
     232          71 :         for (const auto &poDim : m_aoDims)
     233             :         {
     234          82 :             auto poVar = poDim->GetIndexingVariable();
     235          41 :             if (poVar)
     236             :             {
     237          16 :                 const std::string osLabelName(BuildDimensionLabelName(poDim));
     238           8 :                 if (tiledb::ArraySchemaExperimental::has_dimension_label(
     239           8 :                         ctx, *(m_poSchema.get()), osLabelName))
     240             :                 {
     241             :                     auto label =
     242             :                         tiledb::ArraySchemaExperimental::dimension_label(
     243          16 :                             ctx, *(m_poSchema.get()), osLabelName);
     244          16 :                     tiledb::Array labelArray(ctx, label.uri(), TILEDB_WRITE);
     245          16 :                     auto label_attr = labelArray.schema().attribute(0);
     246             :                     const auto eDT =
     247           8 :                         TileDBDataTypeToGDALDataType(label_attr.type());
     248           8 :                     if (eDT != GDT_Unknown)
     249             :                     {
     250          16 :                         std::vector<GByte> abyVals;
     251           8 :                         abyVals.resize(static_cast<size_t>(
     252           8 :                             poVar->GetDimensions()[0]->GetSize() *
     253           8 :                             GDALGetDataTypeSizeBytes(eDT)));
     254           8 :                         GUInt64 anStart[1] = {0};
     255             :                         size_t anCount[1] = {static_cast<size_t>(
     256           8 :                             poVar->GetDimensions()[0]->GetSize())};
     257          16 :                         if (poVar->Read(anStart, anCount, nullptr, nullptr,
     258          16 :                                         GDALExtendedDataType::Create(eDT),
     259           8 :                                         abyVals.data()))
     260             :                         {
     261          16 :                             tiledb::Query query(ctx, labelArray);
     262             :                             query.set_data_buffer(
     263          16 :                                 label_attr.name(),
     264           8 :                                 static_cast<void *>(abyVals.data()),
     265          16 :                                 anCount[0]);
     266           8 :                             if (query.submit() !=
     267             :                                 tiledb::Query::Status::COMPLETE)
     268             :                             {
     269           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     270             :                                          "Could not write values for dimension "
     271             :                                          "label %s",
     272             :                                          osLabelName.c_str());
     273             :                             }
     274             : 
     275           8 :                             if (!poDim->GetType().empty())
     276             :                             {
     277           6 :                                 labelArray.put_metadata(
     278             :                                     DIM_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
     279             :                                     static_cast<uint32_t>(
     280           6 :                                         poDim->GetType().size()),
     281           6 :                                     poDim->GetType().c_str());
     282             :                             }
     283             : 
     284           8 :                             if (!poDim->GetDirection().empty())
     285             :                             {
     286           4 :                                 labelArray.put_metadata(
     287             :                                     DIM_DIRECTION_ATTRIBUTE_NAME,
     288             :                                     TILEDB_STRING_UTF8,
     289             :                                     static_cast<uint32_t>(
     290           4 :                                         poDim->GetDirection().size()),
     291           4 :                                     poDim->GetDirection().c_str());
     292             :                             }
     293             :                         }
     294             :                     }
     295             :                 }
     296             :             }
     297             :         }
     298             :     }
     299           0 :     catch (const std::exception &e)
     300             :     {
     301           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     302           0 :                  "Array %s creation failed with: %s", m_osName.c_str(),
     303           0 :                  e.what());
     304           0 :         return false;
     305             :     }
     306             : 
     307          30 :     return true;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                   TileDBArray::OpenFromDisk()                        */
     312             : /************************************************************************/
     313             : 
     314             : /* static */
     315          45 : std::shared_ptr<TileDBArray> TileDBArray::OpenFromDisk(
     316             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
     317             :     const std::shared_ptr<GDALGroup> &poParent, const std::string &osParentName,
     318             :     const std::string &osName, const std::string &osAttributeName,
     319             :     const std::string &osPath, CSLConstList papszOptions)
     320             : {
     321             :     try
     322             :     {
     323          45 :         auto &ctx = poSharedResource->GetCtx();
     324          45 :         uint64_t nTimestamp = poSharedResource->GetTimestamp();
     325             :         const char *pszTimestamp =
     326          45 :             CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
     327          45 :         if (pszTimestamp)
     328           0 :             nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
     329             : 
     330             :         auto poTileDBArray =
     331          90 :             std::make_unique<tiledb::Array>(ctx, osPath, TILEDB_READ);
     332          45 :         if (nTimestamp > 0)
     333           0 :             poTileDBArray->set_open_timestamp_end(nTimestamp);
     334             : 
     335          90 :         auto schema = poTileDBArray->schema();
     336             : 
     337          45 :         if (schema.attribute_num() != 1 && osAttributeName.empty())
     338             :         {
     339           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     340             :                      "Array %s has %u attributes. "
     341             :                      "osAttributeName must be specified",
     342             :                      osName.c_str(), schema.attribute_num());
     343           0 :             return nullptr;
     344             :         }
     345             : 
     346          45 :         const auto &attr = osAttributeName.empty()
     347             :                                ? schema.attribute(0)
     348          90 :                                : schema.attribute(osAttributeName);
     349          45 :         GDALDataType eDT = TileDBDataTypeToGDALDataType(attr.type());
     350          45 :         if (attr.type() == TILEDB_CHAR)
     351           1 :             eDT = GDT_Byte;
     352          45 :         if (eDT == GDT_Unknown)
     353             :         {
     354           0 :             const char *pszTypeName = "";
     355           0 :             tiledb_datatype_to_str(attr.type(), &pszTypeName);
     356           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     357             :                      "Array %s has type %s, which is unsupported",
     358             :                      osName.c_str(), pszTypeName);
     359           0 :             return nullptr;
     360             :         }
     361             : 
     362          45 :         if (attr.variable_sized())
     363             :         {
     364           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     365             :                      "Variable sized attribute not supported");
     366           0 :             return nullptr;
     367             :         }
     368          45 :         if (attr.cell_val_num() == 2)
     369             :         {
     370           4 :             if (attr.type() == TILEDB_INT16)
     371           1 :                 eDT = GDT_CInt16;
     372           3 :             else if (attr.type() == TILEDB_INT32)
     373           1 :                 eDT = GDT_CInt32;
     374           2 :             else if (attr.type() == TILEDB_FLOAT32)
     375           1 :                 eDT = GDT_CFloat32;
     376           1 :             else if (attr.type() == TILEDB_FLOAT64)
     377           1 :                 eDT = GDT_CFloat64;
     378             :             else
     379             :             {
     380           0 :                 const char *pszTypeName = "";
     381           0 :                 tiledb_datatype_to_str(attr.type(), &pszTypeName);
     382           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     383             :                          "Attribute with number of values per cell = %u not "
     384             :                          "supported for type %s",
     385             :                          attr.cell_val_num(), pszTypeName);
     386           0 :                 return nullptr;
     387             :             }
     388             :         }
     389          41 :         else if (attr.cell_val_num() != 1)
     390             :         {
     391           0 :             CPLError(
     392             :                 CE_Failure, CPLE_NotSupported,
     393             :                 "Attribute with number of values per cell = %u not supported",
     394             :                 attr.cell_val_num());
     395           0 :             return nullptr;
     396             :         }
     397             : 
     398             :         // Compatibility with the 2D raster side: extract X_SIZE, Y_SIZE, SRS
     399             :         // and geotransform
     400          45 :         int nXSize = 0;
     401          45 :         int nYSize = 0;
     402          45 :         std::shared_ptr<OGRSpatialReference> poSRS;
     403          45 :         double adfGeoTransform[6] = {0};
     404          45 :         bool bHasGeoTransform = false;
     405             :         {
     406          45 :             tiledb_datatype_t value_type = TILEDB_ANY;
     407          45 :             uint32_t value_num = 0;
     408          45 :             const void *value = nullptr;
     409          45 :             poTileDBArray->get_metadata(GDAL_ATTRIBUTE_NAME, &value_type,
     410             :                                         &value_num, &value);
     411          49 :             if (value && value_num && value_type == TILEDB_UINT8 &&
     412           4 :                 CPLIsUTF8(static_cast<const char *>(value), value_num))
     413             :             {
     414           8 :                 std::string osXML;
     415           4 :                 osXML.assign(static_cast<const char *>(value), value_num);
     416           4 :                 CPLXMLNode *psRoot = CPLParseXMLString(osXML.c_str());
     417           4 :                 if (psRoot)
     418             :                 {
     419             :                     const CPLXMLNode *psDataset =
     420           4 :                         CPLGetXMLNode(psRoot, "=PAMDataset");
     421           4 :                     if (psDataset)
     422             :                     {
     423           4 :                         for (const CPLXMLNode *psIter = psDataset->psChild;
     424          20 :                              psIter; psIter = psIter->psNext)
     425             :                         {
     426          48 :                             if (psIter->eType == CXT_Element &&
     427          24 :                                 strcmp(psIter->pszValue, "Metadata") == 0 &&
     428           8 :                                 strcmp(CPLGetXMLValue(psIter, "domain", ""),
     429             :                                        "IMAGE_STRUCTURE") == 0)
     430             :                             {
     431           4 :                                 for (const CPLXMLNode *psIter2 =
     432             :                                          psIter->psChild;
     433          32 :                                      psIter2; psIter2 = psIter2->psNext)
     434             :                                 {
     435          80 :                                     if (psIter2->eType == CXT_Element &&
     436          52 :                                         strcmp(psIter2->pszValue, "MDI") == 0 &&
     437          24 :                                         strcmp(
     438             :                                             CPLGetXMLValue(psIter2, "key", ""),
     439             :                                             "X_SIZE") == 0)
     440             :                                     {
     441           4 :                                         nXSize = atoi(CPLGetXMLValue(
     442             :                                             psIter2, nullptr, "0"));
     443             :                                     }
     444          68 :                                     else if (psIter2->eType == CXT_Element &&
     445          20 :                                              strcmp(psIter2->pszValue, "MDI") ==
     446          44 :                                                  0 &&
     447          20 :                                              strcmp(CPLGetXMLValue(psIter2,
     448             :                                                                    "key", ""),
     449             :                                                     "Y_SIZE") == 0)
     450             :                                     {
     451           4 :                                         nYSize = atoi(CPLGetXMLValue(
     452             :                                             psIter2, nullptr, "0"));
     453             :                                     }
     454             :                                 }
     455             :                             }
     456             :                         }
     457             : 
     458             :                         const char *pszSRS =
     459           4 :                             CPLGetXMLValue(psDataset, "SRS", nullptr);
     460           4 :                         if (pszSRS)
     461             :                         {
     462           4 :                             poSRS = std::make_shared<OGRSpatialReference>();
     463           4 :                             poSRS->SetAxisMappingStrategy(
     464             :                                 OAMS_TRADITIONAL_GIS_ORDER);
     465           4 :                             if (poSRS->importFromWkt(pszSRS) != OGRERR_NONE)
     466             :                             {
     467           0 :                                 poSRS.reset();
     468             :                             }
     469             :                         }
     470             : 
     471             :                         const char *pszGeoTransform =
     472           4 :                             CPLGetXMLValue(psDataset, "GeoTransform", nullptr);
     473           4 :                         if (pszGeoTransform)
     474             :                         {
     475             :                             const CPLStringList aosTokens(
     476           8 :                                 CSLTokenizeString2(pszGeoTransform, ", ", 0));
     477           4 :                             if (aosTokens.size() == 6)
     478             :                             {
     479           4 :                                 bHasGeoTransform = true;
     480          28 :                                 for (int i = 0; i < 6; ++i)
     481          24 :                                     adfGeoTransform[i] = CPLAtof(aosTokens[i]);
     482             :                             }
     483             :                         }
     484             :                     }
     485             : 
     486           4 :                     CPLDestroyXMLNode(psRoot);
     487             :                 }
     488             :             }
     489             :         }
     490             : 
     491             :         // Read CRS from _CRS attribute otherwise
     492          45 :         if (!poSRS)
     493             :         {
     494          41 :             tiledb_datatype_t value_type = TILEDB_ANY;
     495          41 :             uint32_t value_num = 0;
     496          41 :             const void *value = nullptr;
     497          41 :             poTileDBArray->get_metadata(CRS_ATTRIBUTE_NAME, &value_type,
     498             :                                         &value_num, &value);
     499          41 :             if (value && value_num &&
     500           2 :                 (value_type == TILEDB_STRING_ASCII ||
     501           2 :                  value_type == TILEDB_STRING_UTF8))
     502             :             {
     503           4 :                 std::string osStr;
     504           2 :                 osStr.assign(static_cast<const char *>(value), value_num);
     505           2 :                 poSRS = std::make_shared<OGRSpatialReference>();
     506           2 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     507           2 :                 if (poSRS->SetFromUserInput(
     508             :                         osStr.c_str(),
     509             :                         OGRSpatialReference::
     510           2 :                             SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     511             :                     OGRERR_NONE)
     512             :                 {
     513           0 :                     poSRS.reset();
     514             :                 }
     515             :             }
     516             :         }
     517             : 
     518             :         // Read unit
     519          90 :         std::string osUnit;
     520             :         {
     521          45 :             tiledb_datatype_t value_type = TILEDB_ANY;
     522          45 :             uint32_t value_num = 0;
     523          45 :             const void *value = nullptr;
     524          45 :             poTileDBArray->get_metadata(UNIT_ATTRIBUTE_NAME, &value_type,
     525             :                                         &value_num, &value);
     526          45 :             if (value && value_num &&
     527           5 :                 (value_type == TILEDB_STRING_ASCII ||
     528           5 :                  value_type == TILEDB_STRING_UTF8))
     529             :             {
     530           5 :                 osUnit.assign(static_cast<const char *>(value), value_num);
     531             :             }
     532             :         }
     533             : 
     534             :         // Read dimensions
     535          90 :         std::vector<std::shared_ptr<GDALDimension>> aoDims;
     536          90 :         const auto dims = schema.domain().dimensions();
     537          90 :         std::vector<GUInt64> anBlockSize;
     538          90 :         std::vector<uint64_t> anStartDimOffset;
     539             :         const std::string osArrayFullName(
     540          90 :             (osParentName == "/" ? std::string() : osParentName) + "/" +
     541          90 :             osName);
     542         106 :         for (size_t i = 0; i < dims.size(); ++i)
     543             :         {
     544          61 :             const auto &dim = dims[i];
     545          61 :             if (dim.type() != TILEDB_UINT64)
     546             :             {
     547           0 :                 const char *pszTypeName = "";
     548           0 :                 tiledb_datatype_to_str(dim.type(), &pszTypeName);
     549           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     550             :                          "Dimension %s of array %s has type %s, which is "
     551             :                          "unsupported. Only UInt64 is supported",
     552           0 :                          dim.name().c_str(), osName.c_str(), pszTypeName);
     553           0 :                 return nullptr;
     554             :             }
     555          61 :             const auto domain = dim.domain<uint64_t>();
     556          61 :             anStartDimOffset.push_back(domain.first);
     557         118 :             const uint64_t nSize = (i + 2 == dims.size() && nYSize > 0) ? nYSize
     558         102 :                                    : (i + 1 == dims.size() && nXSize > 0)
     559          61 :                                        ? nXSize
     560          53 :                                        : domain.second - domain.first + 1;
     561         122 :             std::string osType;
     562         122 :             std::string osDirection;
     563             :             auto poDim = std::make_shared<TileDBDimension>(
     564         122 :                 osArrayFullName, dim.name(), osType, osDirection, nSize);
     565             : 
     566          61 :             const std::string osLabelName(BuildDimensionLabelName(poDim));
     567          61 :             if (tiledb::ArraySchemaExperimental::has_dimension_label(
     568             :                     ctx, schema, osLabelName))
     569             :             {
     570             :                 auto label = tiledb::ArraySchemaExperimental::dimension_label(
     571          16 :                     ctx, schema, osLabelName);
     572             :                 auto poIndexingVar = OpenFromDisk(
     573             :                     poSharedResource, nullptr, osArrayFullName,
     574           8 :                     poDim->GetName(),
     575          16 :                     /* osAttributeName = */ std::string(), label.uri(),
     576          24 :                     /* papszOptions= */ nullptr);
     577           8 :                 if (poIndexingVar)
     578             :                 {
     579             :                     auto poAttr =
     580          16 :                         poIndexingVar->GetAttribute(DIM_TYPE_ATTRIBUTE_NAME);
     581          14 :                     if (poAttr &&
     582          14 :                         poAttr->GetDataType().GetClass() == GEDTC_STRING)
     583             :                     {
     584           6 :                         const char *pszVal = poAttr->ReadAsString();
     585           6 :                         if (pszVal)
     586           6 :                             osType = pszVal;
     587             :                     }
     588             : 
     589          16 :                     poAttr = poIndexingVar->GetAttribute(
     590           8 :                         DIM_DIRECTION_ATTRIBUTE_NAME);
     591          12 :                     if (poAttr &&
     592          12 :                         poAttr->GetDataType().GetClass() == GEDTC_STRING)
     593             :                     {
     594           4 :                         const char *pszVal = poAttr->ReadAsString();
     595           4 :                         if (pszVal)
     596           4 :                             osDirection = pszVal;
     597             :                     }
     598             : 
     599           8 :                     if (!osType.empty() || !osDirection.empty())
     600             :                     {
     601             :                         // Recreate dimension with type and/or direction info
     602          12 :                         poDim = std::make_shared<TileDBDimension>(
     603          12 :                             osArrayFullName, dim.name(), osType, osDirection,
     604           6 :                             nSize);
     605             :                     }
     606             : 
     607           8 :                     poDim->SetIndexingVariableOneTime(poIndexingVar);
     608             :                 }
     609             :             }
     610          79 :             if (bHasGeoTransform && !poDim->GetIndexingVariable() &&
     611          87 :                 i + 2 >= dims.size() && adfGeoTransform[2] == 0 &&
     612           8 :                 adfGeoTransform[4] == 0)
     613             :             {
     614             :                 // Recreate dimension with type and/or direction info
     615           8 :                 if (i + 2 == dims.size())
     616             :                 {
     617           4 :                     osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
     618           4 :                     osDirection = "NORTH";
     619             :                 }
     620             :                 else /* if( i + 1 == dims.size()) */
     621             :                 {
     622           4 :                     osType = GDAL_DIM_TYPE_HORIZONTAL_X;
     623           4 :                     osDirection = "EAST";
     624             :                 }
     625          16 :                 poDim = std::make_shared<TileDBDimension>(
     626          24 :                     osArrayFullName, dim.name(), osType, osDirection, nSize);
     627             :                 // Do not create indexing variable with poDim, otherwise
     628             :                 // both dimension and indexing variable will have a shared_ptr
     629             :                 // to each other, causing memory leak
     630             :                 auto poDimTmp = std::make_shared<GDALDimension>(
     631          16 :                     std::string(), dim.name(), /* osType = */ std::string(),
     632          16 :                     /* osDirection = */ std::string(), nSize);
     633             :                 const double dfStart =
     634           8 :                     (i + 2 == dims.size())
     635           8 :                         ? adfGeoTransform[3] + adfGeoTransform[5] / 2
     636           4 :                         : adfGeoTransform[0] + adfGeoTransform[1] / 2;
     637           8 :                 const double dfStep = (i + 2 == dims.size())
     638           8 :                                           ? adfGeoTransform[5]
     639           8 :                                           : adfGeoTransform[1];
     640          16 :                 poDim->SetIndexingVariableOneTime(
     641          16 :                     GDALMDArrayRegularlySpaced::Create(
     642           8 :                         osArrayFullName, poDim->GetName(), poDimTmp, dfStart,
     643             :                         dfStep, 0));
     644             :             }
     645             : 
     646          61 :             if (poParent && dims.size() >= 2)
     647             :             {
     648          63 :                 for (const auto &osOtherArray : poParent->GetMDArrayNames())
     649             :                 {
     650          43 :                     if (osOtherArray != osName)
     651             :                     {
     652          23 :                         auto poOtherArray = poParent->OpenMDArray(osOtherArray);
     653          44 :                         if (poOtherArray &&
     654          40 :                             poOtherArray->GetDimensionCount() == 1 &&
     655          19 :                             poOtherArray->GetDataType().GetClass() ==
     656          44 :                                 GEDTC_NUMERIC &&
     657          42 :                             poOtherArray->GetAttribute(
     658          42 :                                 std::string("__tiledb_attr.")
     659          19 :                                     .append(poDim->GetName())
     660          38 :                                     .append(".data.standard_name")))
     661             :                         {
     662           2 :                             if (dim.name() == "x")
     663             :                             {
     664           1 :                                 osType = GDAL_DIM_TYPE_HORIZONTAL_X;
     665           1 :                                 osDirection = "EAST";
     666             :                             }
     667           1 :                             else if (dim.name() == "y")
     668             :                             {
     669           1 :                                 osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
     670           1 :                                 osDirection = "NORTH";
     671             :                             }
     672           2 :                             if (!osType.empty())
     673             :                             {
     674           4 :                                 poDim = std::make_shared<TileDBDimension>(
     675           4 :                                     osArrayFullName, dim.name(), osType,
     676           2 :                                     osDirection, nSize);
     677             :                             }
     678           2 :                             poDim->SetIndexingVariableOneTime(poOtherArray);
     679           2 :                             break;
     680             :                         }
     681             :                     }
     682             :                 }
     683             :             }
     684             : 
     685          61 :             aoDims.emplace_back(std::move(poDim));
     686          61 :             anBlockSize.push_back(dim.tile_extent<uint64_t>());
     687             :         }
     688             : 
     689          90 :         GDALExtendedDataType oType = GDALExtendedDataType::Create(eDT);
     690             :         auto poArray = Create(poSharedResource, osParentName, osName, aoDims,
     691          90 :                               oType, osPath);
     692          45 :         poArray->m_poTileDBArray = std::move(poTileDBArray);
     693          90 :         poArray->m_poSchema = std::make_unique<tiledb::ArraySchema>(
     694         135 :             poArray->m_poTileDBArray->schema());
     695          45 :         poArray->m_anBlockSize = std::move(anBlockSize);
     696          45 :         poArray->m_anStartDimOffset = std::move(anStartDimOffset);
     697          45 :         poArray->m_osAttrName = attr.name();
     698          45 :         poArray->m_osUnit = std::move(osUnit);
     699          45 :         poArray->m_nTimestamp = nTimestamp;
     700             : 
     701             :         // Try to get SRS from CF-1 conventions, if dataset has been generated
     702             :         // with https://github.com/TileDB-Inc/TileDB-CF-Py
     703          45 :         if (poParent && !poSRS)
     704             :         {
     705             :             const auto ENDS_WITH_CI =
     706           9 :                 [](const char *pszStr, const char *pszNeedle)
     707             :             {
     708           9 :                 const size_t nLenStr = strlen(pszStr);
     709           9 :                 const size_t nLenNeedle = strlen(pszNeedle);
     710          18 :                 return nLenStr >= nLenNeedle &&
     711           9 :                        memcmp(pszStr + (nLenStr - nLenNeedle), pszNeedle,
     712           9 :                               nLenNeedle) == 0;
     713             :             };
     714             : 
     715             :             const auto GetSRSFromGridMappingArray =
     716           1 :                 [](const std::shared_ptr<GDALMDArray> &poOtherArray,
     717             :                    const std::string &osGMPrefix)
     718             :             {
     719           2 :                 CPLStringList aosGridMappingKeyValues;
     720          11 :                 for (const auto &poGMAttr : poOtherArray->GetAttributes())
     721             :                 {
     722          10 :                     if (STARTS_WITH(poGMAttr->GetName().c_str(),
     723             :                                     osGMPrefix.c_str()))
     724             :                     {
     725             :                         const std::string osKey =
     726          20 :                             poGMAttr->GetName().c_str() + osGMPrefix.size();
     727          10 :                         if (poGMAttr->GetDataType().GetClass() == GEDTC_STRING)
     728             :                         {
     729           2 :                             const char *pszValue = poGMAttr->ReadAsString();
     730           2 :                             if (pszValue)
     731             :                                 aosGridMappingKeyValues.AddNameValue(
     732           2 :                                     osKey.c_str(), pszValue);
     733             :                         }
     734           8 :                         else if (poGMAttr->GetDataType().GetClass() ==
     735             :                                  GEDTC_NUMERIC)
     736             :                         {
     737             :                             const auto aosValues =
     738          16 :                                 poGMAttr->ReadAsDoubleArray();
     739          16 :                             std::string osVal;
     740          17 :                             for (double dfVal : aosValues)
     741             :                             {
     742           9 :                                 if (!osVal.empty())
     743           1 :                                     osVal += ',';
     744           9 :                                 osVal += CPLSPrintf("%.18g", dfVal);
     745             :                             }
     746             :                             aosGridMappingKeyValues.AddNameValue(osKey.c_str(),
     747           8 :                                                                  osVal.c_str());
     748             :                         }
     749             :                     }
     750             :                 }
     751           1 :                 auto l_poSRS = std::make_shared<OGRSpatialReference>();
     752           1 :                 l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     753           1 :                 if (l_poSRS->importFromCF1(aosGridMappingKeyValues.List(),
     754           1 :                                            nullptr) != OGRERR_NONE)
     755             :                 {
     756           0 :                     l_poSRS.reset();
     757             :                 }
     758           2 :                 return l_poSRS;
     759             :             };
     760             : 
     761          62 :             const auto poAttributes = poArray->GetAttributes();
     762          57 :             for (const auto &poAttr : poAttributes)
     763             :             {
     764          27 :                 if (poAttr->GetDataType().GetClass() == GEDTC_STRING &&
     765          19 :                     STARTS_WITH_CI(poAttr->GetName().c_str(),
     766          46 :                                    "__tiledb_attr.") &&
     767           9 :                     ENDS_WITH_CI(poAttr->GetName().c_str(), ".grid_mapping"))
     768             :                 {
     769           1 :                     const char *pszGridMapping = poAttr->ReadAsString();
     770           1 :                     if (pszGridMapping)
     771             :                     {
     772           3 :                         for (const auto &osOtherArray :
     773           8 :                              poParent->GetMDArrayNames())
     774             :                         {
     775           4 :                             if (osOtherArray != osName)
     776             :                             {
     777             :                                 auto poOtherArray =
     778           3 :                                     poParent->OpenMDArray(osOtherArray);
     779           3 :                                 if (poOtherArray)
     780             :                                 {
     781             :                                     const std::string osGMPrefix =
     782           6 :                                         std::string("__tiledb_attr.")
     783           3 :                                             .append(pszGridMapping)
     784           3 :                                             .append(".");
     785             :                                     auto poGridMappingNameAttr =
     786           3 :                                         poOtherArray->GetAttribute(
     787           6 :                                             std::string(osGMPrefix)
     788           3 :                                                 .append("grid_mapping_name")
     789           9 :                                                 .c_str());
     790           3 :                                     if (poGridMappingNameAttr)
     791             :                                     {
     792           2 :                                         poSRS = GetSRSFromGridMappingArray(
     793           1 :                                             poOtherArray, osGMPrefix);
     794           1 :                                         break;
     795             :                                     }
     796             :                                 }
     797             :                             }
     798             :                         }
     799             :                     }
     800           1 :                     break;
     801             :                 }
     802             :             }
     803             :         }
     804             : 
     805             :         // Set SRS DataAxisToSRSAxisMapping
     806          45 :         if (poSRS)
     807             :         {
     808           7 :             int iDimX = 0;
     809           7 :             int iDimY = 0;
     810           7 :             int iCount = 1;
     811          22 :             for (const auto &poDim : aoDims)
     812             :             {
     813          15 :                 if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
     814           5 :                     iDimX = iCount;
     815          10 :                 else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
     816           5 :                     iDimY = iCount;
     817          15 :                 iCount++;
     818             :             }
     819           7 :             if ((iDimX == 0 || iDimY == 0) && aoDims.size() >= 2)
     820             :             {
     821           2 :                 iDimX = static_cast<int>(aoDims.size());
     822           2 :                 iDimY = iDimX - 1;
     823             :             }
     824           7 :             if (iDimX > 0 && iDimY > 0)
     825             :             {
     826           7 :                 if (poSRS->GetDataAxisToSRSAxisMapping() ==
     827          14 :                     std::vector<int>{2, 1})
     828           5 :                     poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
     829           2 :                 else if (poSRS->GetDataAxisToSRSAxisMapping() ==
     830           4 :                          std::vector<int>{1, 2})
     831           2 :                     poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
     832             :             }
     833             :         }
     834             : 
     835          45 :         poArray->m_poSRS = std::move(poSRS);
     836             : 
     837          90 :         const auto filters = attr.filter_list();
     838          90 :         std::string osFilters;
     839          46 :         for (uint32_t j = 0; j < filters.nfilters(); ++j)
     840             :         {
     841           1 :             const auto filter = filters.filter(j);
     842           1 :             if (j > 0)
     843           0 :                 osFilters += ',';
     844           1 :             osFilters += tiledb::Filter::to_str(filter.filter_type());
     845             :         }
     846          45 :         if (!osFilters.empty())
     847             :         {
     848           1 :             poArray->m_aosStructuralInfo.SetNameValue("FILTER_LIST",
     849           1 :                                                       osFilters.c_str());
     850             :         }
     851             : 
     852          45 :         return poArray;
     853             :     }
     854           0 :     catch (const std::exception &e)
     855             :     {
     856           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenFromDisk() failed with: %s",
     857           0 :                  e.what());
     858           0 :         return nullptr;
     859             :     }
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                   TileDBArray::EnsureOpenAs()                        */
     864             : /************************************************************************/
     865             : 
     866         225 : bool TileDBArray::EnsureOpenAs(tiledb_query_type_t mode) const
     867             : {
     868         225 :     if (!m_bFinalized && !Finalize())
     869           0 :         return false;
     870         225 :     if (!m_poTileDBArray)
     871           0 :         return false;
     872         225 :     if (m_poTileDBArray->query_type() == mode && m_poTileDBArray->is_open())
     873         182 :         return true;
     874             :     try
     875             :     {
     876          43 :         m_poTileDBArray->close();
     877          43 :         m_poTileDBArray->open(mode);
     878             :     }
     879           0 :     catch (const std::exception &e)
     880             :     {
     881           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     882           0 :         m_poTileDBArray.reset();
     883           0 :         return false;
     884             :     }
     885          43 :     return true;
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                          TileDBArray::IRead()                        */
     890             : /************************************************************************/
     891             : 
     892          55 : bool TileDBArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     893             :                         const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     894             :                         const GDALExtendedDataType &bufferDataType,
     895             :                         void *pDstBuffer) const
     896             : {
     897          55 :     if (!EnsureOpenAs(TILEDB_READ))
     898           0 :         return false;
     899             : 
     900          55 :     if (!IsStepOneContiguousRowMajorOrderedSameDataType(
     901             :             count, arrayStep, bufferStride, bufferDataType))
     902             :     {
     903           7 :         return ReadUsingContiguousIRead(arrayStartIdx, count, arrayStep,
     904             :                                         bufferStride, bufferDataType,
     905           7 :                                         pDstBuffer);
     906             :     }
     907             :     else
     908             :     {
     909          48 :         std::vector<uint64_t> anSubArray;
     910          48 :         const auto nDims = m_aoDims.size();
     911          48 :         anSubArray.reserve(2 * nDims);
     912             :         size_t nBufferSize =
     913          48 :             GDALDataTypeIsComplex(m_oType.GetNumericDataType()) ? 2 : 1;
     914         113 :         for (size_t i = 0; i < nDims; ++i)
     915             :         {
     916          65 :             anSubArray.push_back(m_anStartDimOffset[i] + arrayStartIdx[i]);
     917          65 :             anSubArray.push_back(m_anStartDimOffset[i] + arrayStartIdx[i] +
     918          65 :                                  count[i] - 1);
     919          65 :             nBufferSize *= count[i];
     920             :         }
     921             :         try
     922             :         {
     923          48 :             tiledb::Query query(m_poSharedResource->GetCtx(),
     924         144 :                                 *(m_poTileDBArray.get()));
     925          48 :             tiledb::Subarray subarray(m_poSharedResource->GetCtx(),
     926          96 :                                       *(m_poTileDBArray.get()));
     927          48 :             subarray.set_subarray(anSubArray);
     928          48 :             query.set_subarray(subarray);
     929          48 :             query.set_data_buffer(m_osAttrName, pDstBuffer, nBufferSize);
     930             : 
     931          48 :             if (m_bStats)
     932           0 :                 tiledb::Stats::enable();
     933             : 
     934          48 :             const auto ret = query.submit();
     935             : 
     936          48 :             if (m_bStats)
     937             :             {
     938           0 :                 tiledb::Stats::dump(stdout);
     939           0 :                 tiledb::Stats::disable();
     940             :             }
     941             : 
     942          48 :             return ret == tiledb::Query::Status::COMPLETE;
     943             :         }
     944           0 :         catch (const std::exception &e)
     945             :         {
     946           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Read() failed with %s",
     947           0 :                      e.what());
     948             :         }
     949             :     }
     950             : 
     951           0 :     return false;
     952             : }
     953             : 
     954             : /************************************************************************/
     955             : /*                          TileDBArray::IWrite()                       */
     956             : /************************************************************************/
     957             : 
     958          23 : bool TileDBArray::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
     959             :                          const GInt64 *arrayStep,
     960             :                          const GPtrDiff_t *bufferStride,
     961             :                          const GDALExtendedDataType &bufferDataType,
     962             :                          const void *pSrcBuffer)
     963             : {
     964          23 :     if (!IsWritable())
     965             :     {
     966           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     967             :                  "Dataset not open in update mode");
     968           0 :         return false;
     969             :     }
     970             : 
     971          23 :     if (!EnsureOpenAs(TILEDB_WRITE))
     972           0 :         return false;
     973             : 
     974          23 :     if (!IsStepOneContiguousRowMajorOrderedSameDataType(
     975             :             count, arrayStep, bufferStride, bufferDataType))
     976             :     {
     977           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     978             :                  "Write parameters not supported");
     979           1 :         return false;
     980             :     }
     981             :     else
     982             :     {
     983          22 :         std::vector<uint64_t> anSubArray;
     984          22 :         const auto nDims = m_aoDims.size();
     985          22 :         anSubArray.reserve(2 * nDims);
     986             :         size_t nBufferSize =
     987          22 :             GDALDataTypeIsComplex(m_oType.GetNumericDataType()) ? 2 : 1;
     988          50 :         for (size_t i = 0; i < nDims; ++i)
     989             :         {
     990          28 :             anSubArray.push_back(m_anStartDimOffset[i] + arrayStartIdx[i]);
     991          28 :             anSubArray.push_back(m_anStartDimOffset[i] + arrayStartIdx[i] +
     992          28 :                                  count[i] - 1);
     993          28 :             nBufferSize *= count[i];
     994             :         }
     995             :         try
     996             :         {
     997          22 :             tiledb::Query query(m_poSharedResource->GetCtx(),
     998          66 :                                 *(m_poTileDBArray.get()));
     999          22 :             tiledb::Subarray subarray(m_poSharedResource->GetCtx(),
    1000          44 :                                       *(m_poTileDBArray.get()));
    1001          22 :             subarray.set_subarray(anSubArray);
    1002          22 :             query.set_subarray(subarray);
    1003          22 :             query.set_data_buffer(m_osAttrName, const_cast<void *>(pSrcBuffer),
    1004          22 :                                   nBufferSize);
    1005             : 
    1006          22 :             return query.submit() == tiledb::Query::Status::COMPLETE;
    1007             :         }
    1008           0 :         catch (const std::exception &e)
    1009             :         {
    1010           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Write() failed with %s",
    1011           0 :                      e.what());
    1012             :         }
    1013             :     }
    1014             : 
    1015           0 :     return false;
    1016             : }
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                  TileDBArray::GetRawNoDataValue()                    */
    1020             : /************************************************************************/
    1021             : 
    1022           9 : const void *TileDBArray::GetRawNoDataValue() const
    1023             : {
    1024           9 :     if (!m_bFinalized)
    1025           0 :         return nullptr;
    1026             : 
    1027           9 :     if (m_abyNoData.empty())
    1028             :     {
    1029           9 :         const void *value = nullptr;
    1030           9 :         uint64_t size = 0;
    1031             :         // Caution: 2 below statements must not be combined in a single one,
    1032             :         // as the lifetime of value is linked to the return value of
    1033             :         // attribute()
    1034          18 :         auto attr = m_poSchema->attribute(m_osAttrName);
    1035           9 :         attr.get_fill_value(&value, &size);
    1036           9 :         if (size == m_oType.GetSize())
    1037             :         {
    1038           9 :             m_abyNoData.resize(size);
    1039           9 :             memcpy(m_abyNoData.data(), value, size);
    1040             :         }
    1041             :     }
    1042             : 
    1043           9 :     return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
    1044             : }
    1045             : 
    1046             : /************************************************************************/
    1047             : /*                  TileDBArray::SetRawNoDataValue()                    */
    1048             : /************************************************************************/
    1049             : 
    1050           3 : bool TileDBArray::SetRawNoDataValue(const void *pRawNoData)
    1051             : {
    1052           3 :     if (m_bFinalized)
    1053             :     {
    1054           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1055             :                  "SetRawNoDataValue() not supported after array has been "
    1056             :                  "finalized.");
    1057           1 :         return false;
    1058             :     }
    1059             : 
    1060           2 :     if (pRawNoData)
    1061             :     {
    1062           2 :         CPLAssert(m_poAttr);
    1063           2 :         m_poAttr->set_fill_value(pRawNoData, m_oType.GetSize());
    1064           2 :         m_abyNoData.resize(m_oType.GetSize());
    1065           2 :         memcpy(m_abyNoData.data(), pRawNoData, m_oType.GetSize());
    1066             :     }
    1067             : 
    1068           2 :     Finalize();
    1069             : 
    1070           2 :     return true;
    1071             : }
    1072             : 
    1073             : /************************************************************************/
    1074             : /*                  TileDBArray::CreateAttribute()                      */
    1075             : /************************************************************************/
    1076             : 
    1077          12 : std::shared_ptr<GDALAttribute> TileDBArray::CreateAttribute(
    1078             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
    1079             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    1080             : {
    1081          12 :     return CreateAttributeImpl(osName, anDimensions, oDataType, papszOptions);
    1082             : }
    1083             : 
    1084             : /************************************************************************/
    1085             : /*                   TileDBArray::GetAttribute()                        */
    1086             : /************************************************************************/
    1087             : 
    1088             : std::shared_ptr<GDALAttribute>
    1089          39 : TileDBArray::GetAttribute(const std::string &osName) const
    1090             : {
    1091          39 :     return GetAttributeImpl(osName);
    1092             : }
    1093             : 
    1094             : /************************************************************************/
    1095             : /*                   TileDBArray::GetAttributes()                       */
    1096             : /************************************************************************/
    1097             : 
    1098             : std::vector<std::shared_ptr<GDALAttribute>>
    1099          40 : TileDBArray::GetAttributes(CSLConstList papszOptions) const
    1100             : {
    1101          40 :     return GetAttributesImpl(papszOptions);
    1102             : }
    1103             : 
    1104             : /************************************************************************/
    1105             : /*                   TileDBArray::DeleteAttribute()                     */
    1106             : /************************************************************************/
    1107             : 
    1108           0 : bool TileDBArray::DeleteAttribute(const std::string &osName,
    1109             :                                   CSLConstList papszOptions)
    1110             : {
    1111           0 :     return DeleteAttributeImpl(osName, papszOptions);
    1112             : }
    1113             : 
    1114             : /************************************************************************/
    1115             : /*                   TileDBArray::SetSpatialRef()                       */
    1116             : /************************************************************************/
    1117             : 
    1118           2 : bool TileDBArray::SetSpatialRef(const OGRSpatialReference *poSRS)
    1119             : {
    1120           2 :     if (!IsWritable())
    1121             :     {
    1122           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1123             :                  "Dataset not open in update mode");
    1124           0 :         return false;
    1125             :     }
    1126             : 
    1127           2 :     if (!EnsureOpenAs(TILEDB_WRITE))
    1128           0 :         return false;
    1129             : 
    1130             :     try
    1131             :     {
    1132           2 :         if (m_poSRS && !poSRS)
    1133           0 :             m_poTileDBArray->delete_metadata(CRS_ATTRIBUTE_NAME);
    1134             : 
    1135           2 :         m_poSRS.reset();
    1136           2 :         if (poSRS)
    1137             :         {
    1138           2 :             m_poSRS.reset(poSRS->Clone());
    1139             : 
    1140           2 :             char *pszPROJJSON = nullptr;
    1141           2 :             if (m_poSRS->exportToPROJJSON(&pszPROJJSON, nullptr) ==
    1142           4 :                     OGRERR_NONE &&
    1143           2 :                 pszPROJJSON != nullptr)
    1144             :             {
    1145           4 :                 m_poTileDBArray->put_metadata(
    1146             :                     CRS_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    1147           2 :                     static_cast<uint32_t>(strlen(pszPROJJSON)), pszPROJJSON);
    1148           2 :                 CPLFree(pszPROJJSON);
    1149             :             }
    1150             :             else
    1151             :             {
    1152           0 :                 CPLFree(pszPROJJSON);
    1153           0 :                 return false;
    1154             :             }
    1155             :         }
    1156           2 :         return true;
    1157             :     }
    1158           0 :     catch (const std::exception &e)
    1159             :     {
    1160           0 :         CPLError(CE_Failure, CPLE_AppDefined, "SetSpatialRef() failed with: %s",
    1161           0 :                  e.what());
    1162           0 :         return false;
    1163             :     }
    1164             : }
    1165             : 
    1166             : /************************************************************************/
    1167             : /*                       TileDBArray::SetUnit()                         */
    1168             : /************************************************************************/
    1169             : 
    1170           5 : bool TileDBArray::SetUnit(const std::string &osUnit)
    1171             : {
    1172           5 :     if (!IsWritable())
    1173             :     {
    1174           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1175             :                  "Dataset not open in update mode");
    1176           0 :         return false;
    1177             :     }
    1178             : 
    1179           5 :     if (!EnsureOpenAs(TILEDB_WRITE))
    1180           0 :         return false;
    1181             : 
    1182             :     try
    1183             :     {
    1184           5 :         if (!m_osUnit.empty() && osUnit.empty())
    1185           0 :             m_poTileDBArray->delete_metadata(UNIT_ATTRIBUTE_NAME);
    1186             : 
    1187           5 :         m_osUnit = osUnit;
    1188           5 :         if (!osUnit.empty())
    1189             :         {
    1190          10 :             m_poTileDBArray->put_metadata(
    1191             :                 UNIT_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
    1192           5 :                 static_cast<uint32_t>(osUnit.size()), osUnit.data());
    1193             :         }
    1194           5 :         return true;
    1195             :     }
    1196           0 :     catch (const std::exception &e)
    1197             :     {
    1198           0 :         CPLError(CE_Failure, CPLE_AppDefined, "SetUnit() failed with: %s",
    1199           0 :                  e.what());
    1200           0 :         return false;
    1201             :     }
    1202             : }
    1203             : 
    1204             : /************************************************************************/
    1205             : /*                          FillBlockSize()                             */
    1206             : /************************************************************************/
    1207             : 
    1208             : static bool
    1209          30 : FillBlockSize(const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
    1210             :               const GDALExtendedDataType &oDataType,
    1211             :               std::vector<GUInt64> &anBlockSize, CSLConstList papszOptions)
    1212             : {
    1213          30 :     const auto nDims = aoDimensions.size();
    1214          30 :     anBlockSize.resize(nDims);
    1215          71 :     for (size_t i = 0; i < nDims; ++i)
    1216          41 :         anBlockSize[i] = 1;
    1217          30 :     if (nDims >= 2)
    1218             :     {
    1219          18 :         anBlockSize[nDims - 2] =
    1220          18 :             std::min(std::max<GUInt64>(1, aoDimensions[nDims - 2]->GetSize()),
    1221          18 :                      static_cast<GUInt64>(256));
    1222          18 :         anBlockSize[nDims - 1] =
    1223          18 :             std::min(std::max<GUInt64>(1, aoDimensions[nDims - 1]->GetSize()),
    1224          27 :                      static_cast<GUInt64>(256));
    1225             :     }
    1226          21 :     else if (nDims == 1)
    1227             :     {
    1228          21 :         anBlockSize[0] = std::max<GUInt64>(1, aoDimensions[0]->GetSize());
    1229             :     }
    1230             : 
    1231          30 :     const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
    1232          30 :     if (pszBlockSize)
    1233             :     {
    1234             :         const auto aszTokens(
    1235           1 :             CPLStringList(CSLTokenizeString2(pszBlockSize, ",", 0)));
    1236           1 :         if (static_cast<size_t>(aszTokens.size()) != nDims)
    1237             :         {
    1238           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1239             :                      "Invalid number of values in BLOCKSIZE");
    1240           0 :             return false;
    1241             :         }
    1242           1 :         size_t nBlockSize = oDataType.GetSize();
    1243           3 :         for (size_t i = 0; i < nDims; ++i)
    1244             :         {
    1245           2 :             anBlockSize[i] = static_cast<GUInt64>(CPLAtoGIntBig(aszTokens[i]));
    1246           2 :             if (anBlockSize[i] == 0)
    1247             :             {
    1248           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1249             :                          "Values in BLOCKSIZE should be > 0");
    1250           0 :                 return false;
    1251             :             }
    1252           2 :             if (anBlockSize[i] >
    1253           2 :                 std::numeric_limits<size_t>::max() / nBlockSize)
    1254             :             {
    1255           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1256             :                          "Too large values in BLOCKSIZE");
    1257           0 :                 return false;
    1258             :             }
    1259           2 :             nBlockSize *= static_cast<size_t>(anBlockSize[i]);
    1260             :         }
    1261             :     }
    1262          30 :     return true;
    1263             : }
    1264             : 
    1265             : /************************************************************************/
    1266             : /*                 TileDBArray::GDALDataTypeToTileDB()                  */
    1267             : /************************************************************************/
    1268             : 
    1269          52 : /*static*/ bool TileDBArray::GDALDataTypeToTileDB(GDALDataType dt,
    1270             :                                                   tiledb_datatype_t &tiledb_dt)
    1271             : {
    1272          52 :     switch (dt)
    1273             :     {
    1274           4 :         case GDT_Byte:
    1275           4 :             tiledb_dt = TILEDB_UINT8;
    1276           4 :             break;
    1277           1 :         case GDT_Int8:
    1278           1 :             tiledb_dt = TILEDB_INT8;
    1279           1 :             break;
    1280           1 :         case GDT_UInt16:
    1281           1 :             tiledb_dt = TILEDB_UINT16;
    1282           1 :             break;
    1283           2 :         case GDT_CInt16:
    1284             :         case GDT_Int16:
    1285           2 :             tiledb_dt = TILEDB_INT16;
    1286           2 :             break;
    1287           1 :         case GDT_UInt32:
    1288           1 :             tiledb_dt = TILEDB_UINT32;
    1289           1 :             break;
    1290           7 :         case GDT_CInt32:
    1291             :         case GDT_Int32:
    1292           7 :             tiledb_dt = TILEDB_INT32;
    1293           7 :             break;
    1294           1 :         case GDT_UInt64:
    1295           1 :             tiledb_dt = TILEDB_UINT64;
    1296           1 :             break;
    1297           1 :         case GDT_Int64:
    1298           1 :             tiledb_dt = TILEDB_INT64;
    1299           1 :             break;
    1300           8 :         case GDT_CFloat32:
    1301             :         case GDT_Float32:
    1302           8 :             tiledb_dt = TILEDB_FLOAT32;
    1303           8 :             break;
    1304          26 :         case GDT_CFloat64:
    1305             :         case GDT_Float64:
    1306          26 :             tiledb_dt = TILEDB_FLOAT64;
    1307          26 :             break;
    1308             : 
    1309           0 :         case GDT_Unknown:
    1310             :         case GDT_TypeCount:
    1311             :         {
    1312           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type: %s",
    1313             :                      GDALGetDataTypeName(dt));
    1314           0 :             return false;
    1315             :         }
    1316             :     }
    1317          52 :     return true;
    1318             : }
    1319             : 
    1320             : /************************************************************************/
    1321             : /*                 IsIncreasingOrDecreasing1DVar()                      */
    1322             : /************************************************************************/
    1323             : 
    1324             : static void
    1325           8 : IsIncreasingOrDecreasing1DVar(const std::shared_ptr<GDALMDArray> &poVar,
    1326             :                               bool &bIncreasing, bool &bDecreasing)
    1327             : {
    1328           8 :     bIncreasing = false;
    1329           8 :     bDecreasing = false;
    1330          16 :     std::vector<double> adfVals;
    1331             :     try
    1332             :     {
    1333           8 :         adfVals.resize(
    1334           8 :             static_cast<size_t>(poVar->GetDimensions()[0]->GetSize()));
    1335             :     }
    1336           0 :     catch (const std::exception &)
    1337             :     {
    1338             :     }
    1339           8 :     if (adfVals.size() > 1)
    1340             :     {
    1341           8 :         GUInt64 anStart[1] = {0};
    1342           8 :         size_t anCount[1] = {adfVals.size()};
    1343          16 :         if (poVar->Read(anStart, anCount, nullptr, nullptr,
    1344          16 :                         GDALExtendedDataType::Create(GDT_Float64),
    1345           8 :                         adfVals.data()))
    1346             :         {
    1347           8 :             if (adfVals[1] > adfVals[0])
    1348           5 :                 bIncreasing = true;
    1349           3 :             else if (adfVals[1] < adfVals[0])
    1350           3 :                 bDecreasing = true;
    1351           8 :             if (bIncreasing || bDecreasing)
    1352             :             {
    1353          34 :                 for (size_t i = 2; i < adfVals.size(); ++i)
    1354             :                 {
    1355          26 :                     if (bIncreasing)
    1356             :                     {
    1357          16 :                         if (!(adfVals[i] > adfVals[i - 1]))
    1358             :                         {
    1359           0 :                             bIncreasing = false;
    1360           0 :                             break;
    1361             :                         }
    1362             :                     }
    1363             :                     else
    1364             :                     {
    1365          10 :                         if (!(adfVals[i] < adfVals[i - 1]))
    1366             :                         {
    1367           0 :                             bDecreasing = false;
    1368           0 :                             break;
    1369             :                         }
    1370             :                     }
    1371             :                 }
    1372             :             }
    1373             :         }
    1374             :     }
    1375           8 : }
    1376             : 
    1377             : /************************************************************************/
    1378             : /*                   TileDBArray::CreateOnDisk()                        */
    1379             : /************************************************************************/
    1380             : 
    1381             : /* static */
    1382          34 : std::shared_ptr<TileDBArray> TileDBArray::CreateOnDisk(
    1383             :     const std::shared_ptr<TileDBSharedResource> &poSharedResource,
    1384             :     const std::shared_ptr<TileDBGroup> &poParent, const std::string &osName,
    1385             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
    1386             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    1387             : {
    1388          34 :     if (aoDimensions.empty())
    1389             :     {
    1390           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1391             :                  "Zero-dimensions arrays are not supported by TileDB");
    1392           1 :         return nullptr;
    1393             :     }
    1394             : 
    1395          33 :     tiledb_datatype_t tiledb_dt = TILEDB_ANY;
    1396          33 :     if (oDataType.GetClass() != GEDTC_NUMERIC)
    1397             :     {
    1398           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1399             :                  "Only numeric data types are supported");
    1400           1 :         return nullptr;
    1401             :     }
    1402          32 :     if (!GDALDataTypeToTileDB(oDataType.GetNumericDataType(), tiledb_dt))
    1403           0 :         return nullptr;
    1404             : 
    1405             :     try
    1406             :     {
    1407             :         const auto osSanitizedName =
    1408          64 :             TileDBSharedResource::SanitizeNameForPath(osName);
    1409          63 :         if (osSanitizedName.empty() || STARTS_WITH(osName.c_str(), "./") ||
    1410          31 :             STARTS_WITH(osName.c_str(), "../") ||
    1411          94 :             STARTS_WITH(osName.c_str(), ".\\") ||
    1412          31 :             STARTS_WITH(osName.c_str(), "..\\"))
    1413             :         {
    1414           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid array name");
    1415           1 :             return nullptr;
    1416             :         }
    1417          62 :         std::string osArrayPath = poParent->GetPath() + "/" + osSanitizedName;
    1418          31 :         const char *pszURI = CSLFetchNameValue(papszOptions, "URI");
    1419          31 :         if (pszURI)
    1420           0 :             osArrayPath = pszURI;
    1421             : 
    1422          31 :         auto &ctx = poSharedResource->GetCtx();
    1423          62 :         tiledb::VFS vfs(ctx);
    1424          31 :         if (vfs.is_dir(osArrayPath))
    1425             :         {
    1426           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Path %s already exists",
    1427             :                      osArrayPath.c_str());
    1428           1 :             return nullptr;
    1429             :         }
    1430             : 
    1431          60 :         std::vector<GUInt64> anBlockSize;
    1432             : #if defined(__GNUC__)
    1433             : #pragma GCC diagnostic push
    1434             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    1435             : #endif
    1436          30 :         if (!FillBlockSize(aoDimensions, oDataType, anBlockSize, papszOptions))
    1437           0 :             return nullptr;
    1438             : #if defined(__GNUC__)
    1439             : #pragma GCC diagnostic pop
    1440             : #endif
    1441             :         auto poSchema =
    1442          60 :             std::make_unique<tiledb::ArraySchema>(ctx, TILEDB_DENSE);
    1443          30 :         poSchema->set_tile_order(TILEDB_ROW_MAJOR);
    1444          30 :         poSchema->set_cell_order(TILEDB_ROW_MAJOR);
    1445             : 
    1446          60 :         tiledb::FilterList filterList(ctx);
    1447             :         const char *pszCompression =
    1448          30 :             CSLFetchNameValue(papszOptions, "COMPRESSION");
    1449             :         const char *pszCompressionLevel =
    1450          30 :             CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
    1451             : 
    1452          30 :         if (pszCompression != nullptr)
    1453             :         {
    1454           1 :             int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
    1455           1 :             if (TileDBDataset::AddFilter(ctx, filterList, pszCompression,
    1456           1 :                                          nLevel) != CE_None)
    1457             :             {
    1458           0 :                 return nullptr;
    1459             :             }
    1460             :         }
    1461          30 :         poSchema->set_coords_filter_list(filterList);
    1462             : 
    1463          60 :         tiledb::Domain domain(ctx);
    1464          71 :         for (size_t i = 0; i < aoDimensions.size(); ++i)
    1465             :         {
    1466          41 :             const auto &poDim = aoDimensions[i];
    1467          41 :             if (poDim->GetSize() == 0)
    1468             :             {
    1469           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid dim size: 0");
    1470           0 :                 return nullptr;
    1471             :             }
    1472          82 :             std::string osDimName(poDim->GetName());
    1473          41 :             if (poDim->GetName() == osName)
    1474           5 :                 osDimName += "_dim";
    1475             :             auto dim = tiledb::Dimension::create<uint64_t>(
    1476          82 :                 ctx, osDimName, {0, poDim->GetSize() - 1}, anBlockSize[i]);
    1477          41 :             domain.add_dimension(std::move(dim));
    1478             :         }
    1479             : 
    1480          30 :         poSchema->set_domain(domain);
    1481             : 
    1482          60 :         std::vector<std::shared_ptr<GDALMDArray>> apoIndexingVariables;
    1483          71 :         for (size_t i = 0; i < aoDimensions.size(); ++i)
    1484             :         {
    1485          41 :             const auto &poDim = aoDimensions[i];
    1486          82 :             auto poIndexingVar = poDim->GetIndexingVariable();
    1487          41 :             bool bDimLabelCreated = false;
    1488          41 :             tiledb_datatype_t dim_label_tiledb_dt = TILEDB_ANY;
    1489          57 :             if (poIndexingVar && poIndexingVar->GetDimensionCount() == 1 &&
    1490          16 :                 poIndexingVar->GetDataType().GetClass() == GEDTC_NUMERIC &&
    1491           8 :                 poIndexingVar->GetDimensions()[0]->GetName() ==
    1492          16 :                     poDim->GetName() &&
    1493           8 :                 poIndexingVar->GetDimensions()[0]->GetSize() <
    1494           8 :                     10 * 1024 * 1024 &&
    1495           8 :                 !GDALDataTypeIsComplex(
    1496          57 :                     poIndexingVar->GetDataType().GetNumericDataType()) &&
    1497           8 :                 GDALDataTypeToTileDB(
    1498           8 :                     poIndexingVar->GetDataType().GetNumericDataType(),
    1499             :                     dim_label_tiledb_dt))
    1500             :             {
    1501           8 :                 bool bIncreasing = false;
    1502           8 :                 bool bDecreasing = false;
    1503           8 :                 IsIncreasingOrDecreasing1DVar(poIndexingVar, bIncreasing,
    1504             :                                               bDecreasing);
    1505           8 :                 if (bIncreasing || bDecreasing)
    1506             :                 {
    1507           8 :                     bDimLabelCreated = true;
    1508           8 :                     apoIndexingVariables.push_back(poIndexingVar);
    1509          16 :                     tiledb::ArraySchemaExperimental::add_dimension_label(
    1510           8 :                         ctx, *(poSchema.get()), static_cast<uint32_t>(i),
    1511          16 :                         BuildDimensionLabelName(poDim),
    1512             :                         bIncreasing ? TILEDB_INCREASING_DATA
    1513             :                                     : TILEDB_DECREASING_DATA,
    1514             :                         dim_label_tiledb_dt,
    1515          16 :                         std::optional<tiledb::FilterList>(filterList));
    1516             :                 }
    1517             :             }
    1518          41 :             if (poIndexingVar && !bDimLabelCreated)
    1519             :             {
    1520           0 :                 CPLDebug("TILEDB",
    1521             :                          "Dimension %s has indexing variable %s, "
    1522             :                          "but not compatible of a dimension label",
    1523           0 :                          poDim->GetName().c_str(),
    1524           0 :                          poIndexingVar->GetName().c_str());
    1525             :             }
    1526             :         }
    1527             : 
    1528             :         auto attr = std::make_unique<tiledb::Attribute>(
    1529          60 :             tiledb::Attribute::create(ctx, osName, tiledb_dt));
    1530          30 :         if (GDALDataTypeIsComplex(oDataType.GetNumericDataType()))
    1531           4 :             attr->set_cell_val_num(2);
    1532          30 :         attr->set_filter_list(filterList);
    1533             : 
    1534             :         // Implement a deferred TileDB array creation given that we might
    1535             :         // need to set the fill value of the attribute from the nodata value
    1536          30 :         auto poArray = Create(poSharedResource, poParent->GetFullName(), osName,
    1537          60 :                               aoDimensions, oDataType, osArrayPath);
    1538          30 :         poArray->m_bFinalized = false;
    1539          30 :         poArray->m_poParent = poParent;
    1540          30 :         poArray->m_osParentPath = poParent->GetPath();
    1541          30 :         poArray->m_poSchema = std::move(poSchema);
    1542          30 :         poArray->m_osAttrName = attr->name();
    1543          30 :         poArray->m_poAttr = std::move(attr);
    1544          30 :         poArray->m_anBlockSize = std::move(anBlockSize);
    1545          30 :         poArray->m_anStartDimOffset.resize(aoDimensions.size());
    1546             :         // To keep a reference on the indexing variables, so they are still
    1547             :         // alive at Finalize() time
    1548          30 :         poArray->m_apoIndexingVariables = std::move(apoIndexingVariables);
    1549          30 :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "STATS", "FALSE")))
    1550           0 :             poArray->m_bStats = true;
    1551             : 
    1552          30 :         uint64_t nTimestamp = poSharedResource->GetTimestamp();
    1553             :         const char *pszTimestamp =
    1554          30 :             CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
    1555          30 :         if (pszTimestamp)
    1556           0 :             nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
    1557          30 :         poArray->m_nTimestamp = nTimestamp;
    1558             : 
    1559          30 :         return poArray;
    1560             :     }
    1561           0 :     catch (const std::exception &e)
    1562             :     {
    1563           0 :         CPLError(CE_Failure, CPLE_AppDefined, "CreateMDArray() failed with: %s",
    1564           0 :                  e.what());
    1565           0 :         return nullptr;
    1566             :     }
    1567             : }
    1568             : 
    1569             : /************************************************************************/
    1570             : /*                    TileDBArray::GetStructuralInfo()                  */
    1571             : /************************************************************************/
    1572             : 
    1573           7 : CSLConstList TileDBArray::GetStructuralInfo() const
    1574             : {
    1575           7 :     return m_aosStructuralInfo.List();
    1576             : }

Generated by: LCOV version 1.14