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 : }
|