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

Generated by: LCOV version 1.14