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