Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL TileDB Driver
4 : * Purpose: Implement GDAL TileDB Support based on https://www.tiledb.io
5 : * Author: TileDB, Inc
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, TileDB, Inc
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include <cassert>
14 : #include <cmath>
15 : #include <cinttypes>
16 : #include <limits>
17 :
18 : #include "gdal_priv_templates.hpp"
19 : #include "tiledbheaders.h"
20 :
21 : // XML element inside _gdal XML metadata to store the number of overview levels
22 : constexpr const char *OVERVIEW_COUNT_KEY = "tiledb:OverviewCount";
23 :
24 : /************************************************************************/
25 : /* ==================================================================== */
26 : /* TileDBRasterBand */
27 : /* ==================================================================== */
28 : /************************************************************************/
29 :
30 : class TileDBRasterBand final : public GDALPamRasterBand
31 : {
32 : friend class TileDBRasterDataset;
33 :
34 : protected:
35 : TileDBRasterDataset *poGDS;
36 : bool bStats;
37 : CPLString osAttrName;
38 : double m_dfNoData = 0;
39 : bool m_bNoDataSet = false;
40 :
41 : CPL_DISALLOW_COPY_ASSIGN(TileDBRasterBand)
42 :
43 : public:
44 : TileDBRasterBand(TileDBRasterDataset *, int,
45 : const std::string &osAttr = TILEDB_VALUES);
46 : CPLErr IReadBlock(int, int, void *) override;
47 : CPLErr IWriteBlock(int, int, void *) override;
48 : CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
49 : GDALDataType, GSpacing, GSpacing,
50 : GDALRasterIOExtraArg *psExtraArg) override;
51 : GDALColorInterp GetColorInterpretation() override;
52 :
53 : double GetNoDataValue(int *pbHasNoData) override;
54 : CPLErr SetNoDataValue(double dfNoData) override;
55 :
56 : int GetOverviewCount() override;
57 : GDALRasterBand *GetOverview(int nIdx) override;
58 : };
59 :
60 226 : static CPLErr option_to_index_type(const char *pszIndexingType,
61 : TILEDB_INTERLEAVE_MODE &eMode)
62 : {
63 226 : CPLErr eErr = CE_None;
64 :
65 226 : if (pszIndexingType)
66 : {
67 139 : if EQUAL (pszIndexingType, "BAND")
68 108 : eMode = BAND;
69 31 : else if EQUAL (pszIndexingType, "ATTRIBUTES")
70 8 : eMode = ATTRIBUTES;
71 23 : else if EQUAL (pszIndexingType, "PIXEL")
72 23 : eMode = PIXEL;
73 : else
74 : {
75 0 : eErr = CE_Failure;
76 0 : CPLError(eErr, CPLE_AppDefined,
77 : "Unable to identify TileDB index mode %s.",
78 : pszIndexingType);
79 : }
80 : }
81 : else
82 : {
83 87 : eMode = BAND;
84 : }
85 :
86 226 : return eErr;
87 : }
88 :
89 106 : static const char *index_type_name(TILEDB_INTERLEAVE_MODE eMode)
90 : {
91 106 : switch (eMode)
92 : {
93 8 : case PIXEL:
94 8 : return "PIXEL";
95 5 : case ATTRIBUTES:
96 5 : return "ATTRIBUTES";
97 93 : case BAND:
98 93 : return "BAND";
99 0 : default:
100 0 : return nullptr;
101 : }
102 : }
103 :
104 : /************************************************************************/
105 : /* SetBuffer() */
106 : /************************************************************************/
107 :
108 402 : static CPLErr SetBuffer(tiledb::Query *poQuery, GDALDataType eType,
109 : const CPLString &osAttrName, void *pImage, size_t nSize)
110 : {
111 402 : switch (eType)
112 : {
113 200 : case GDT_UInt8:
114 : poQuery->set_data_buffer(
115 200 : osAttrName, reinterpret_cast<unsigned char *>(pImage), nSize);
116 200 : break;
117 2 : case GDT_Int8:
118 : poQuery->set_data_buffer(osAttrName,
119 2 : reinterpret_cast<int8_t *>(pImage), nSize);
120 2 : break;
121 3 : case GDT_UInt16:
122 : poQuery->set_data_buffer(
123 3 : osAttrName, reinterpret_cast<unsigned short *>(pImage), nSize);
124 3 : break;
125 3 : case GDT_UInt32:
126 : poQuery->set_data_buffer(
127 3 : osAttrName, reinterpret_cast<unsigned int *>(pImage), nSize);
128 3 : break;
129 2 : case GDT_UInt64:
130 : poQuery->set_data_buffer(
131 2 : osAttrName, reinterpret_cast<uint64_t *>(pImage), nSize);
132 2 : break;
133 3 : case GDT_Int16:
134 : poQuery->set_data_buffer(osAttrName,
135 3 : reinterpret_cast<short *>(pImage), nSize);
136 3 : break;
137 9 : case GDT_Int32:
138 : poQuery->set_data_buffer(osAttrName,
139 9 : reinterpret_cast<int *>(pImage), nSize);
140 9 : break;
141 2 : case GDT_Int64:
142 : poQuery->set_data_buffer(
143 2 : osAttrName, reinterpret_cast<int64_t *>(pImage), nSize);
144 2 : break;
145 159 : case GDT_Float32:
146 : poQuery->set_data_buffer(osAttrName,
147 159 : reinterpret_cast<float *>(pImage), nSize);
148 159 : break;
149 3 : case GDT_Float64:
150 : poQuery->set_data_buffer(osAttrName,
151 3 : reinterpret_cast<double *>(pImage), nSize);
152 3 : break;
153 3 : case GDT_CInt16:
154 : poQuery->set_data_buffer(
155 3 : osAttrName, reinterpret_cast<short *>(pImage), nSize * 2);
156 3 : break;
157 3 : case GDT_CInt32:
158 : poQuery->set_data_buffer(
159 3 : osAttrName, reinterpret_cast<int *>(pImage), nSize * 2);
160 3 : break;
161 3 : case GDT_CFloat32:
162 : poQuery->set_data_buffer(
163 3 : osAttrName, reinterpret_cast<float *>(pImage), nSize * 2);
164 3 : break;
165 7 : case GDT_CFloat64:
166 : poQuery->set_data_buffer(
167 7 : osAttrName, reinterpret_cast<double *>(pImage), nSize * 2);
168 7 : break;
169 0 : default:
170 0 : return CE_Failure;
171 : }
172 402 : return CE_None;
173 : }
174 :
175 : /************************************************************************/
176 : /* TileDBRasterBand() */
177 : /************************************************************************/
178 :
179 928 : TileDBRasterBand::TileDBRasterBand(TileDBRasterDataset *poDSIn, int nBandIn,
180 928 : const std::string &osAttr)
181 928 : : poGDS(poDSIn), bStats(poDSIn->bStats), osAttrName(osAttr)
182 : {
183 928 : poDS = poDSIn;
184 928 : nBand = nBandIn;
185 928 : eDataType = poGDS->eDataType;
186 928 : if (eDataType == GDT_Unknown)
187 : {
188 : try
189 : {
190 14 : auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
191 14 : ->schema()
192 28 : .attribute(osAttr);
193 14 : switch (attr.type())
194 : {
195 1 : case TILEDB_INT8:
196 1 : eDataType = GDT_Int8;
197 1 : break;
198 1 : case TILEDB_UINT8:
199 1 : eDataType = GDT_UInt8;
200 1 : break;
201 2 : case TILEDB_INT16:
202 2 : eDataType =
203 2 : attr.cell_val_num() == 2 ? GDT_CInt16 : GDT_Int16;
204 2 : break;
205 1 : case TILEDB_UINT16:
206 1 : eDataType = GDT_UInt16;
207 1 : break;
208 2 : case TILEDB_INT32:
209 2 : eDataType =
210 2 : attr.cell_val_num() == 2 ? GDT_CInt32 : GDT_Int32;
211 2 : break;
212 1 : case TILEDB_UINT32:
213 1 : eDataType = GDT_UInt32;
214 1 : break;
215 1 : case TILEDB_INT64:
216 1 : eDataType = GDT_Int64;
217 1 : break;
218 1 : case TILEDB_UINT64:
219 1 : eDataType = GDT_UInt64;
220 1 : break;
221 2 : case TILEDB_FLOAT32:
222 2 : eDataType =
223 2 : attr.cell_val_num() == 2 ? GDT_CFloat32 : GDT_Float32;
224 2 : break;
225 2 : case TILEDB_FLOAT64:
226 2 : eDataType =
227 2 : attr.cell_val_num() == 2 ? GDT_CFloat64 : GDT_Float64;
228 2 : break;
229 0 : default:
230 : {
231 0 : const char *pszTypeName = "";
232 0 : tiledb_datatype_to_str(attr.type(), &pszTypeName);
233 0 : CPLError(CE_Failure, CPLE_NotSupported,
234 : "Unhandled TileDB data type: %s", pszTypeName);
235 0 : break;
236 : }
237 : }
238 : }
239 0 : catch (const tiledb::TileDBError &e)
240 : {
241 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
242 : }
243 : }
244 928 : eAccess = poGDS->eAccess;
245 928 : nRasterXSize = poGDS->nRasterXSize;
246 928 : nRasterYSize = poGDS->nRasterYSize;
247 928 : nBlockXSize = poGDS->nBlockXSize;
248 928 : nBlockYSize = poGDS->nBlockYSize;
249 928 : }
250 :
251 : /************************************************************************/
252 : /* IRasterIO() */
253 : /************************************************************************/
254 :
255 344 : CPLErr TileDBRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
256 : int nXSize, int nYSize, void *pData,
257 : int nBufXSize, int nBufYSize,
258 : GDALDataType eBufType, GSpacing nPixelSpace,
259 : GSpacing nLineSpace,
260 : GDALRasterIOExtraArg *psExtraArg)
261 : {
262 344 : if (!poGDS->m_bDeferredCreateHasRun)
263 5 : poGDS->DeferredCreate(/* bCreateArray = */ true);
264 344 : if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
265 0 : return CE_Failure;
266 344 : if (!poGDS->m_array)
267 : {
268 1 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
269 1 : return CE_Failure;
270 : }
271 :
272 343 : if (poGDS->eIndexMode == ATTRIBUTES && eRWFlag == GF_Write)
273 : {
274 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
275 : "Unable to write using band ordered IRasterIO when using "
276 : "interleave 'ATTRIBUTES'.");
277 0 : return CE_Failure;
278 : }
279 :
280 343 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
281 :
282 343 : if (eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
283 280 : nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
284 280 : nLineSpace == nBufXSize * nPixelSpace)
285 : {
286 280 : const uint64_t nBandIdx = poGDS->nBandStart + nBand - 1;
287 : std::vector<uint64_t> oaSubarray = {
288 : nBandIdx,
289 : nBandIdx,
290 280 : static_cast<uint64_t>(nYOff),
291 280 : static_cast<uint64_t>(nYOff) + nYSize - 1,
292 280 : static_cast<uint64_t>(nXOff),
293 560 : static_cast<uint64_t>(nXOff) + nXSize - 1};
294 280 : if (poGDS->eIndexMode == PIXEL)
295 84 : std::rotate(oaSubarray.begin(), oaSubarray.begin() + 2,
296 84 : oaSubarray.end());
297 :
298 : try
299 : {
300 280 : tiledb::Context *ctx = poGDS->m_ctx.get();
301 280 : const auto &oArray = poGDS->GetArray(eRWFlag == GF_Write, ctx);
302 :
303 560 : auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
304 560 : tiledb::Subarray subarray(*ctx, oArray);
305 280 : if (poGDS->m_array->schema().domain().ndim() == 3)
306 : {
307 213 : subarray.set_subarray(oaSubarray);
308 : }
309 : else
310 : {
311 268 : subarray.set_subarray(std::vector<uint64_t>(
312 201 : oaSubarray.cbegin() + 2, oaSubarray.cend()));
313 : }
314 280 : poQuery->set_subarray(subarray);
315 :
316 280 : const size_t nValues = static_cast<size_t>(nBufXSize) * nBufYSize;
317 280 : SetBuffer(poQuery.get(), eDataType, osAttrName, pData, nValues);
318 :
319 : // write additional co-registered values
320 560 : std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
321 :
322 280 : if (poGDS->m_lpoAttributeDS.size() > 0)
323 : {
324 12 : const int nXSizeToRead = nXOff + nXSize > nRasterXSize
325 6 : ? nRasterXSize - nXOff
326 : : nXSize;
327 12 : const int nYSizeToRead = nYOff + nYSize > nRasterYSize
328 6 : ? nRasterYSize - nYOff
329 : : nYSize;
330 18 : for (auto const &poAttrDS : poGDS->m_lpoAttributeDS)
331 : {
332 12 : GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(nBand);
333 12 : GDALDataType eAttrType = poAttrBand->GetRasterDataType();
334 12 : int nBytes = GDALGetDataTypeSizeBytes(eAttrType);
335 12 : void *pAttrBlock = VSI_MALLOC_VERBOSE(nBytes * nValues);
336 :
337 12 : if (pAttrBlock == nullptr)
338 : {
339 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
340 : "Cannot allocate attribute buffer");
341 0 : return CE_Failure;
342 : }
343 12 : aBlocks.emplace_back(pAttrBlock, &VSIFree);
344 :
345 12 : poAttrBand->AdviseRead(nXOff, nYOff, nXSizeToRead,
346 : nYSizeToRead, nXSizeToRead,
347 12 : nYSizeToRead, eAttrType, nullptr);
348 :
349 12 : CPLErr eErr = poAttrBand->RasterIO(
350 : GF_Read, nXOff, nYOff, nXSizeToRead, nYSizeToRead,
351 : pAttrBlock, nXSizeToRead, nYSizeToRead, eAttrType,
352 : nPixelSpace, nLineSpace, psExtraArg);
353 :
354 12 : if (eErr == CE_None)
355 : {
356 : CPLString osName =
357 24 : CPLGetBasenameSafe(poAttrDS->GetDescription());
358 :
359 12 : SetBuffer(poQuery.get(), eAttrType, osName, pAttrBlock,
360 : nValues);
361 : }
362 : else
363 : {
364 0 : return eErr;
365 : }
366 : }
367 : }
368 :
369 280 : if (bStats)
370 0 : tiledb::Stats::enable();
371 :
372 280 : auto status = poQuery->submit();
373 :
374 280 : if (bStats)
375 : {
376 0 : tiledb::Stats::dump(stdout);
377 0 : tiledb::Stats::disable();
378 : }
379 :
380 280 : if (status == tiledb::Query::Status::FAILED)
381 0 : return CE_Failure;
382 : else
383 280 : return CE_None;
384 : }
385 0 : catch (const tiledb::TileDBError &e)
386 : {
387 0 : CPLError(CE_Failure, CPLE_AppDefined,
388 : "TileDB: TileDBRasterBand::IRasterIO() failed: %s",
389 0 : e.what());
390 0 : return CE_Failure;
391 : }
392 : }
393 :
394 63 : return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
395 : pData, nBufXSize, nBufYSize, eBufType,
396 63 : nPixelSpace, nLineSpace, psExtraArg);
397 : }
398 :
399 140 : CPLErr TileDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
400 : void *pImage)
401 : {
402 140 : const int nXOff = nBlockXOff * nBlockXSize;
403 140 : const int nYOff = nBlockYOff * nBlockYSize;
404 140 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
405 280 : return IRasterIO(GF_Read, nXOff, nYOff, nBlockXSize, nBlockYSize, pImage,
406 : nBlockXSize, nBlockYSize, eDataType, nDTSize,
407 140 : nDTSize * nBlockXSize, nullptr);
408 : }
409 :
410 : /************************************************************************/
411 : /* IWriteBlock() */
412 : /************************************************************************/
413 :
414 15 : CPLErr TileDBRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
415 : void *pImage)
416 :
417 : {
418 15 : if (eAccess == GA_ReadOnly)
419 : {
420 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
421 : "Unable to write block, dataset is opened read only.");
422 0 : return CE_Failure;
423 : }
424 :
425 15 : CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
426 : pImage != nullptr);
427 :
428 15 : int nStartX = nBlockXSize * nBlockXOff;
429 15 : int nStartY = nBlockYSize * nBlockYOff;
430 :
431 15 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
432 30 : return IRasterIO(GF_Write, nStartX, nStartY, nBlockXSize, nBlockYSize,
433 : pImage, nBlockXSize, nBlockYSize, eDataType, nDTSize,
434 15 : nDTSize * nBlockXSize, nullptr);
435 : }
436 :
437 : /************************************************************************/
438 : /* GetColorInterpretation() */
439 : /************************************************************************/
440 :
441 71 : GDALColorInterp TileDBRasterBand::GetColorInterpretation()
442 :
443 : {
444 71 : if (poGDS->nBands == 1)
445 20 : return GCI_GrayIndex;
446 :
447 51 : if (nBand == 1)
448 20 : return GCI_RedBand;
449 :
450 31 : else if (nBand == 2)
451 15 : return GCI_GreenBand;
452 :
453 16 : else if (nBand == 3)
454 16 : return GCI_BlueBand;
455 :
456 0 : return GCI_AlphaBand;
457 : }
458 :
459 : /************************************************************************/
460 : /* GetNoDataValue() */
461 : /************************************************************************/
462 :
463 82 : double TileDBRasterBand::GetNoDataValue(int *pbHasNoData)
464 : {
465 82 : if (pbHasNoData)
466 78 : *pbHasNoData = false;
467 82 : if (m_bNoDataSet)
468 : {
469 11 : if (pbHasNoData)
470 11 : *pbHasNoData = true;
471 11 : return m_dfNoData;
472 : }
473 71 : if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
474 0 : return 0.0;
475 71 : if (!poGDS->m_array)
476 0 : return 0.0;
477 71 : double dfNoData = 0.0;
478 : try
479 : {
480 71 : const void *value = nullptr;
481 71 : uint64_t size = 0;
482 : // Caution: 2 below statements must not be combined in a single one,
483 : // as the lifetime of value is linked to the return value of
484 : // attribute()
485 71 : auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
486 71 : ->schema()
487 142 : .attribute(osAttrName);
488 71 : attr.get_fill_value(&value, &size);
489 71 : if (value &&
490 71 : size == static_cast<uint64_t>(GDALGetDataTypeSizeBytes(eDataType)))
491 : {
492 71 : switch (eDataType)
493 : {
494 54 : case GDT_UInt8:
495 54 : dfNoData = *static_cast<const uint8_t *>(value);
496 54 : break;
497 1 : case GDT_Int8:
498 1 : dfNoData = *static_cast<const int8_t *>(value);
499 1 : break;
500 1 : case GDT_UInt16:
501 1 : dfNoData = *static_cast<const uint16_t *>(value);
502 1 : break;
503 2 : case GDT_Int16:
504 : case GDT_CInt16:
505 2 : dfNoData = *static_cast<const int16_t *>(value);
506 2 : break;
507 1 : case GDT_UInt32:
508 1 : dfNoData = *static_cast<const uint32_t *>(value);
509 1 : break;
510 2 : case GDT_Int32:
511 : case GDT_CInt32:
512 2 : dfNoData = *static_cast<const int32_t *>(value);
513 2 : break;
514 0 : case GDT_UInt64:
515 0 : dfNoData = static_cast<double>(
516 0 : *static_cast<const uint64_t *>(value));
517 0 : break;
518 0 : case GDT_Int64:
519 0 : dfNoData = static_cast<double>(
520 0 : *static_cast<const int64_t *>(value));
521 0 : break;
522 0 : case GDT_Float16:
523 : case GDT_CFloat16:
524 : // tileDB does not support float16
525 0 : CPLAssert(false);
526 : break;
527 5 : case GDT_Float32:
528 : case GDT_CFloat32:
529 5 : dfNoData = *static_cast<const float *>(value);
530 5 : break;
531 5 : case GDT_Float64:
532 : case GDT_CFloat64:
533 5 : dfNoData = *static_cast<const double *>(value);
534 5 : break;
535 0 : case GDT_Unknown:
536 : case GDT_TypeCount:
537 0 : break;
538 : }
539 71 : if (pbHasNoData)
540 67 : *pbHasNoData = true;
541 : }
542 : }
543 0 : catch (const tiledb::TileDBError &e)
544 : {
545 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
546 : }
547 71 : return dfNoData;
548 : }
549 :
550 : /************************************************************************/
551 : /* IsValidNoData() */
552 : /************************************************************************/
553 :
554 37 : template <class T> static bool IsValidNoData(double dfNoData)
555 : {
556 72 : return GDALIsValueInRange<T>(dfNoData) &&
557 72 : dfNoData == static_cast<double>(static_cast<T>(dfNoData));
558 : }
559 :
560 : /************************************************************************/
561 : /* SetNoDataValue() */
562 : /************************************************************************/
563 :
564 45 : CPLErr TileDBRasterBand::SetNoDataValue(double dfNoData)
565 : {
566 45 : if (poGDS->m_bDeferredCreateHasBeenSuccessful)
567 : {
568 1 : CPLError(CE_Failure, CPLE_NotSupported,
569 : "TileDBRasterBand::SetNoDataValue(): cannot be called after "
570 : "pixel values have been set");
571 1 : return CE_Failure;
572 : }
573 :
574 56 : if (nBand != 1 &&
575 : dfNoData !=
576 12 : cpl::down_cast<TileDBRasterBand *>(poGDS->papoBands[0])->m_dfNoData)
577 : {
578 1 : CPLError(CE_Failure, CPLE_NotSupported,
579 : "TileDBRasterBand::SetNoDataValue(): all bands should have "
580 : "the same nodata value");
581 1 : return CE_Failure;
582 : }
583 :
584 43 : bool bIsValid = false;
585 43 : switch (eDataType)
586 : {
587 26 : case GDT_UInt8:
588 26 : bIsValid = IsValidNoData<uint8_t>(dfNoData);
589 26 : break;
590 1 : case GDT_Int8:
591 1 : bIsValid = IsValidNoData<int8_t>(dfNoData);
592 1 : break;
593 1 : case GDT_UInt16:
594 1 : bIsValid = IsValidNoData<uint16_t>(dfNoData);
595 1 : break;
596 2 : case GDT_Int16:
597 : case GDT_CInt16:
598 2 : bIsValid = IsValidNoData<int16_t>(dfNoData);
599 2 : break;
600 1 : case GDT_UInt32:
601 1 : bIsValid = IsValidNoData<uint32_t>(dfNoData);
602 1 : break;
603 2 : case GDT_Int32:
604 : case GDT_CInt32:
605 2 : bIsValid = IsValidNoData<int32_t>(dfNoData);
606 2 : break;
607 0 : case GDT_UInt64:
608 0 : bIsValid = IsValidNoData<uint64_t>(dfNoData);
609 0 : break;
610 0 : case GDT_Int64:
611 0 : bIsValid = IsValidNoData<int64_t>(dfNoData);
612 0 : break;
613 0 : case GDT_Float16:
614 : case GDT_CFloat16:
615 : // tileDB does not support float16
616 0 : bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
617 0 : break;
618 5 : case GDT_Float32:
619 : case GDT_CFloat32:
620 5 : bIsValid = CPLIsNan(dfNoData) || IsValidNoData<float>(dfNoData);
621 5 : break;
622 5 : case GDT_Float64:
623 : case GDT_CFloat64:
624 5 : bIsValid = true;
625 5 : break;
626 0 : case GDT_Unknown:
627 : case GDT_TypeCount:
628 0 : break;
629 : }
630 43 : if (!bIsValid)
631 : {
632 3 : CPLError(CE_Failure, CPLE_NotSupported,
633 : "TileDBRasterBand::SetNoDataValue(): nodata value cannot be "
634 : "stored in band data type");
635 3 : return CE_Failure;
636 : }
637 :
638 40 : m_dfNoData = dfNoData;
639 40 : m_bNoDataSet = true;
640 40 : return CE_None;
641 : }
642 :
643 : /************************************************************************/
644 : /* GetOverviewCount() */
645 : /************************************************************************/
646 :
647 15 : int TileDBRasterBand::GetOverviewCount()
648 : {
649 15 : if (poGDS->m_nOverviewCountFromMetadata > 0)
650 : {
651 7 : poGDS->LoadOverviews();
652 7 : return static_cast<int>(poGDS->m_apoOverviewDS.size());
653 : }
654 8 : return GDALPamRasterBand::GetOverviewCount();
655 : }
656 :
657 : /************************************************************************/
658 : /* GetOverview() */
659 : /************************************************************************/
660 :
661 22 : GDALRasterBand *TileDBRasterBand::GetOverview(int nIdx)
662 : {
663 22 : if (poGDS->m_nOverviewCountFromMetadata > 0)
664 : {
665 21 : poGDS->LoadOverviews();
666 21 : if (nIdx >= 0 && nIdx < static_cast<int>(poGDS->m_apoOverviewDS.size()))
667 : {
668 17 : return poGDS->m_apoOverviewDS[nIdx]->GetRasterBand(nBand);
669 : }
670 4 : return nullptr;
671 : }
672 1 : return GDALPamRasterBand::GetOverview(nIdx);
673 : }
674 :
675 : /************************************************************************/
676 : /* ==================================================================== */
677 : /* TileDBRasterDataset */
678 : /* ==================================================================== */
679 : /************************************************************************/
680 :
681 : /************************************************************************/
682 : /* ~TileDBRasterDataset() */
683 : /************************************************************************/
684 :
685 498 : TileDBRasterDataset::~TileDBRasterDataset()
686 :
687 : {
688 249 : TileDBRasterDataset::CloseDependentDatasets();
689 249 : TileDBRasterDataset::Close();
690 498 : }
691 :
692 : /************************************************************************/
693 : /* GetArray() */
694 : /************************************************************************/
695 :
696 307 : tiledb::Array &TileDBRasterDataset::GetArray(bool bForWrite,
697 : tiledb::Context *&ctx)
698 : {
699 307 : if (eAccess == GA_Update && !bForWrite)
700 : {
701 105 : if (!m_roArray)
702 : {
703 34 : m_roCtx = std::make_unique<tiledb::Context>(m_ctx->config());
704 34 : if (nTimestamp)
705 : {
706 16 : m_roArray = std::make_unique<tiledb::Array>(
707 8 : *m_roCtx, m_osArrayURI, TILEDB_READ,
708 24 : tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
709 : }
710 : else
711 : {
712 52 : m_roArray = std::make_unique<tiledb::Array>(
713 78 : *m_roCtx, m_osArrayURI, TILEDB_READ);
714 : }
715 : }
716 :
717 105 : ctx = m_roCtx.get();
718 105 : return *(m_roArray.get());
719 : }
720 :
721 202 : ctx = m_ctx.get();
722 202 : return *(m_array.get());
723 : }
724 :
725 : /************************************************************************/
726 : /* IRasterio() */
727 : /************************************************************************/
728 :
729 85 : CPLErr TileDBRasterDataset::IRasterIO(
730 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
731 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
732 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
733 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
734 :
735 : {
736 85 : if (!m_bDeferredCreateHasRun)
737 48 : DeferredCreate(/* bCreateArray = */ true);
738 85 : if (!m_bDeferredCreateHasBeenSuccessful)
739 0 : return CE_Failure;
740 85 : if (!m_array)
741 : {
742 1 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has been closed");
743 1 : return CE_Failure;
744 : }
745 :
746 : // support special case of writing attributes for bands, all attributes have to be set at once
747 84 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
748 :
749 84 : if (eIndexMode == ATTRIBUTES && nBandCount == nBands &&
750 4 : eBufType == eDataType && nXSize == nBufXSize && nYSize == nBufYSize &&
751 4 : nBufferDTSize > 0 && nPixelSpace == nBufferDTSize &&
752 4 : nLineSpace == nBufXSize * nPixelSpace &&
753 1 : ((nBandSpace == 0 && nBandCount == 1) ||
754 3 : nBandSpace == nBufYSize * nLineSpace))
755 : {
756 : std::vector<uint64_t> oaSubarray = {
757 4 : static_cast<uint64_t>(nYOff),
758 4 : static_cast<uint64_t>(nYOff) + nYSize - 1,
759 4 : static_cast<uint64_t>(nXOff),
760 8 : static_cast<uint64_t>(nXOff) + nXSize - 1};
761 :
762 : try
763 : {
764 4 : tiledb::Context *ctx = m_ctx.get();
765 4 : const auto &oArray = GetArray(eRWFlag == GF_Write, ctx);
766 :
767 8 : auto poQuery = std::make_unique<tiledb::Query>(*ctx, oArray);
768 8 : tiledb::Subarray subarray(*ctx, oArray);
769 4 : subarray.set_subarray(oaSubarray);
770 4 : poQuery->set_subarray(subarray);
771 :
772 14 : for (int b = 0; b < nBandCount; b++)
773 : {
774 10 : TileDBRasterBand *poBand = cpl::down_cast<TileDBRasterBand *>(
775 10 : GetRasterBand(panBandMap[b]));
776 10 : const size_t nRegionSize =
777 10 : static_cast<size_t>(nBufXSize) * nBufYSize * nBufferDTSize;
778 10 : SetBuffer(poQuery.get(), eDataType, poBand->osAttrName,
779 10 : static_cast<GByte *>(pData) + b * nRegionSize,
780 : nRegionSize);
781 : }
782 :
783 4 : if (bStats)
784 0 : tiledb::Stats::enable();
785 :
786 4 : auto status = poQuery->submit();
787 :
788 4 : if (bStats)
789 : {
790 0 : tiledb::Stats::dump(stdout);
791 0 : tiledb::Stats::disable();
792 : }
793 :
794 4 : if (status == tiledb::Query::Status::FAILED)
795 0 : return CE_Failure;
796 : else
797 4 : return CE_None;
798 : }
799 0 : catch (const tiledb::TileDBError &e)
800 : {
801 0 : CPLError(CE_Failure, CPLE_AppDefined,
802 : "TileDB: TileDBRasterDataset::IRasterIO() failed: %s",
803 0 : e.what());
804 0 : return CE_Failure;
805 : }
806 : }
807 :
808 80 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
809 : pData, nBufXSize, nBufYSize, eBufType,
810 : nBandCount, panBandMap, nPixelSpace,
811 80 : nLineSpace, nBandSpace, psExtraArg);
812 : }
813 :
814 : /************************************************************************/
815 : /* AddDimensions() */
816 : /************************************************************************/
817 :
818 117 : CPLErr TileDBRasterDataset::AddDimensions(tiledb::Domain &domain,
819 : const char *pszAttrName,
820 : tiledb::Dimension &y,
821 : tiledb::Dimension &x,
822 : tiledb::Dimension *poBands)
823 :
824 : {
825 117 : CPLErr eErr = CE_None;
826 :
827 : const bool bHasFillValue =
828 117 : nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_bNoDataSet
829 117 : : false;
830 : const double dfFillValue =
831 117 : nBands ? cpl::down_cast<TileDBRasterBand *>(papoBands[0])->m_dfNoData
832 117 : : 0.0;
833 :
834 117 : switch (eIndexMode)
835 : {
836 6 : case ATTRIBUTES:
837 6 : domain.add_dimensions(y, x);
838 6 : eErr = CreateAttribute(eDataType, pszAttrName, nBands,
839 : bHasFillValue, dfFillValue);
840 6 : break;
841 8 : case PIXEL:
842 8 : assert(poBands);
843 8 : domain.add_dimensions(y, x, *poBands);
844 8 : eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
845 : dfFillValue);
846 8 : break;
847 103 : default: // BAND
848 103 : assert(poBands);
849 103 : domain.add_dimensions(*poBands, y, x);
850 103 : eErr = CreateAttribute(eDataType, pszAttrName, 1, bHasFillValue,
851 : dfFillValue);
852 103 : break;
853 : }
854 :
855 117 : return eErr;
856 : }
857 :
858 : /************************************************************************/
859 : /* Close() */
860 : /************************************************************************/
861 :
862 487 : CPLErr TileDBRasterDataset::Close(GDALProgressFunc, void *)
863 : {
864 487 : CPLErr eErr = CE_None;
865 487 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
866 : {
867 249 : if (TileDBRasterDataset::FlushCache(true) != CE_None)
868 0 : eErr = CE_Failure;
869 :
870 249 : if (!m_bDeferredCreateHasRun)
871 62 : DeferredCreate(/* bCreateArray = */ true);
872 :
873 249 : if (nPamFlags & GPF_DIRTY)
874 135 : TrySaveXML();
875 :
876 : try
877 : {
878 249 : if (m_array)
879 : {
880 246 : m_array->close();
881 246 : m_array.reset();
882 : }
883 : }
884 0 : catch (const tiledb::TileDBError &e)
885 : {
886 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
887 : }
888 : try
889 : {
890 249 : if (m_roArray)
891 : {
892 34 : m_roArray->close();
893 34 : m_roArray.reset();
894 : }
895 : }
896 0 : catch (const tiledb::TileDBError &e)
897 : {
898 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
899 : }
900 :
901 249 : if (GDALPamDataset::Close() != CE_None)
902 0 : eErr = CE_Failure;
903 : }
904 487 : return eErr;
905 : }
906 :
907 : /************************************************************************/
908 : /* CloseDependentDatasets() */
909 : /************************************************************************/
910 :
911 249 : int TileDBRasterDataset::CloseDependentDatasets()
912 : {
913 249 : int bDroppedRef = GDALPamDataset::CloseDependentDatasets();
914 249 : if (!m_apoOverviewDS.empty())
915 6 : bDroppedRef = TRUE;
916 249 : m_apoOverviewDS.clear();
917 249 : return bDroppedRef;
918 : }
919 :
920 : /************************************************************************/
921 : /* FlushCache() */
922 : /************************************************************************/
923 :
924 315 : CPLErr TileDBRasterDataset::FlushCache(bool bAtClosing)
925 :
926 : {
927 315 : return BlockBasedFlushCache(bAtClosing);
928 : }
929 :
930 : /************************************************************************/
931 : /* TrySaveXML() */
932 : /************************************************************************/
933 :
934 135 : CPLErr TileDBRasterDataset::TrySaveXML()
935 :
936 : {
937 135 : if (m_array == nullptr)
938 0 : return CE_None;
939 :
940 135 : CPLXMLNode *psTree = nullptr;
941 : try
942 : {
943 135 : nPamFlags &= ~GPF_DIRTY;
944 :
945 135 : if (psPam == nullptr || (nPamFlags & GPF_NOSAVE))
946 0 : return CE_None;
947 :
948 : /* --------------------------------------------------------------------
949 : */
950 : /* Make sure we know the filename we want to store in. */
951 : /* --------------------------------------------------------------------
952 : */
953 135 : if (!BuildPamFilename())
954 0 : return CE_None;
955 :
956 : /* --------------------------------------------------------------------
957 : */
958 : /* Build the XML representation of the auxiliary metadata. */
959 : /* --------------------------------------------------------------------
960 : */
961 135 : psTree = SerializeToXML(nullptr);
962 :
963 135 : if (psTree == nullptr)
964 : {
965 : /* If we have unset all metadata, we have to delete the PAM file */
966 0 : m_array->delete_metadata(GDAL_ATTRIBUTE_NAME);
967 :
968 0 : if (m_bDatasetInGroup)
969 : {
970 0 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
971 0 : group.delete_metadata(GDAL_ATTRIBUTE_NAME);
972 : }
973 :
974 0 : return CE_None;
975 : }
976 :
977 135 : if (m_poSubDatasetsTree)
978 : {
979 3 : CPLAddXMLChild(psTree,
980 3 : CPLCloneXMLTree(m_poSubDatasetsTree->psChild));
981 : }
982 :
983 : /* --------------------------------------------------------------------
984 : */
985 : /* If we are working with a subdataset, we need to integrate */
986 : /* the subdataset tree within the whole existing pam tree, */
987 : /* after removing any old version of the same subdataset. */
988 : /* --------------------------------------------------------------------
989 : */
990 135 : if (!psPam->osSubdatasetName.empty())
991 : {
992 : CPLXMLNode *psOldTree, *psSubTree;
993 :
994 0 : CPLErrorReset();
995 : {
996 0 : CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
997 0 : psOldTree = CPLParseXMLFile(psPam->pszPamFilename);
998 : }
999 :
1000 0 : if (psOldTree == nullptr)
1001 : psOldTree =
1002 0 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset");
1003 :
1004 0 : for (psSubTree = psOldTree->psChild; psSubTree != nullptr;
1005 0 : psSubTree = psSubTree->psNext)
1006 : {
1007 0 : if (psSubTree->eType != CXT_Element ||
1008 0 : !EQUAL(psSubTree->pszValue, "Subdataset"))
1009 0 : continue;
1010 :
1011 0 : if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
1012 : psPam->osSubdatasetName))
1013 0 : continue;
1014 :
1015 0 : break;
1016 : }
1017 :
1018 0 : if (psSubTree == nullptr)
1019 : {
1020 : psSubTree =
1021 0 : CPLCreateXMLNode(psOldTree, CXT_Element, "Subdataset");
1022 0 : CPLCreateXMLNode(
1023 : CPLCreateXMLNode(psSubTree, CXT_Attribute, "name"),
1024 0 : CXT_Text, psPam->osSubdatasetName);
1025 : }
1026 :
1027 : CPLXMLNode *psOldPamDataset =
1028 0 : CPLGetXMLNode(psSubTree, "PAMDataset");
1029 0 : if (psOldPamDataset != nullptr)
1030 : {
1031 0 : CPLRemoveXMLChild(psSubTree, psOldPamDataset);
1032 0 : CPLDestroyXMLNode(psOldPamDataset);
1033 : }
1034 :
1035 0 : CPLAddXMLChild(psSubTree, psTree);
1036 0 : psTree = psOldTree;
1037 : }
1038 :
1039 135 : if (m_nOverviewCountFromMetadata)
1040 : {
1041 4 : CPLCreateXMLElementAndValue(
1042 : psTree, OVERVIEW_COUNT_KEY,
1043 : CPLSPrintf("%d", m_nOverviewCountFromMetadata));
1044 : }
1045 :
1046 : /* --------------------------------------------------------------------
1047 : */
1048 : /* Try saving the auxiliary metadata. */
1049 : /* --------------------------------------------------------------------
1050 : */
1051 270 : CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
1052 135 : char *pszTree = CPLSerializeXMLTree(psTree);
1053 :
1054 0 : std::unique_ptr<tiledb::Array> poArrayToClose;
1055 135 : tiledb::Array *poArray = m_array.get();
1056 135 : if (eAccess == GA_ReadOnly)
1057 : {
1058 1 : if (nTimestamp)
1059 : {
1060 0 : poArrayToClose = std::make_unique<tiledb::Array>(
1061 0 : *m_ctx, m_array->uri(), TILEDB_WRITE,
1062 0 : tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp));
1063 : }
1064 : else
1065 : {
1066 2 : poArrayToClose = std::make_unique<tiledb::Array>(
1067 3 : *m_ctx, m_array->uri(), TILEDB_WRITE);
1068 : }
1069 1 : poArray = poArrayToClose.get();
1070 : }
1071 :
1072 135 : poArray->put_metadata(DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
1073 : static_cast<int>(strlen(RASTER_DATASET_TYPE)),
1074 : RASTER_DATASET_TYPE);
1075 135 : poArray->put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_UINT8,
1076 135 : static_cast<int>(strlen(pszTree)), pszTree);
1077 :
1078 135 : if (poArrayToClose)
1079 1 : poArrayToClose->close();
1080 :
1081 135 : if (m_bDatasetInGroup)
1082 : {
1083 369 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
1084 123 : group.put_metadata(GDAL_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
1085 123 : static_cast<int>(strlen(pszTree)), pszTree);
1086 123 : group.close();
1087 : }
1088 :
1089 135 : CPLFree(pszTree);
1090 :
1091 : /* --------------------------------------------------------------------
1092 : */
1093 : /* Cleanup */
1094 : /* --------------------------------------------------------------------
1095 : */
1096 135 : if (psTree)
1097 135 : CPLDestroyXMLNode(psTree);
1098 :
1099 135 : return CE_None;
1100 : }
1101 0 : catch (const std::exception &e)
1102 : {
1103 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1104 0 : if (psTree)
1105 0 : CPLDestroyXMLNode(psTree);
1106 0 : return CE_Failure;
1107 : }
1108 : }
1109 :
1110 : /************************************************************************/
1111 : /* TryLoadXML() */
1112 : /************************************************************************/
1113 :
1114 129 : CPLErr TileDBRasterDataset::TryLoadXML(CSLConstList papszSiblingFiles)
1115 :
1116 : {
1117 129 : return TryLoadCachedXML(papszSiblingFiles, true);
1118 : }
1119 :
1120 : /************************************************************************/
1121 : /* TryLoadCachedXML() */
1122 : /************************************************************************/
1123 :
1124 257 : CPLErr TileDBRasterDataset::TryLoadCachedXML(CSLConstList /*papszSiblingFiles*/,
1125 : bool bReload)
1126 :
1127 : {
1128 257 : CPLXMLNode *psTree = nullptr;
1129 : try
1130 : {
1131 257 : PamInitialize();
1132 514 : tiledb::VFS vfs(*m_ctx, m_ctx->config());
1133 :
1134 : /* --------------------------------------------------------------------
1135 : */
1136 : /* Clear dirty flag. Generally when we get to this point is */
1137 : /* from a call at the end of the Open() method, and some calls */
1138 : /* may have already marked the PAM info as dirty (for instance */
1139 : /* setting metadata), but really everything to this point is */
1140 : /* reproducible, and so the PAM info should not really be */
1141 : /* thought of as dirty. */
1142 : /* --------------------------------------------------------------------
1143 : */
1144 257 : nPamFlags &= ~GPF_DIRTY;
1145 :
1146 : /* --------------------------------------------------------------------
1147 : */
1148 : /* Try reading the file. */
1149 : /* --------------------------------------------------------------------
1150 : */
1151 257 : if (!BuildPamFilename())
1152 0 : return CE_None;
1153 :
1154 : /* --------------------------------------------------------------------
1155 : */
1156 : /* In case the PAM filename is a .aux.xml file next to the */
1157 : /* physical file and we have a siblings list, then we can skip */
1158 : /* stat'ing the filesystem. */
1159 : /* --------------------------------------------------------------------
1160 : */
1161 257 : CPLErr eLastErr = CPLGetLastErrorType();
1162 257 : int nLastErrNo = CPLGetLastErrorNo();
1163 514 : CPLString osLastErrorMsg = CPLGetLastErrorMsg();
1164 :
1165 257 : CPLErrorReset();
1166 : {
1167 514 : CPLErrorHandlerPusher oQuietError(CPLQuietErrorHandler);
1168 :
1169 257 : if (bReload)
1170 : {
1171 129 : tiledb_datatype_t v_type =
1172 : TILEDB_UINT8; // CPLSerializeXMLTree returns char*
1173 129 : const void *v_r = nullptr;
1174 129 : uint32_t v_num = 0;
1175 23 : auto &oArray = ((eAccess == GA_Update) && (m_roArray))
1176 : ? m_roArray
1177 152 : : m_array;
1178 129 : oArray->get_metadata(GDAL_ATTRIBUTE_NAME, &v_type, &v_num,
1179 : &v_r);
1180 128 : if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
1181 0 : v_type == TILEDB_STRING_ASCII ||
1182 129 : v_type == TILEDB_STRING_UTF8))
1183 : {
1184 : osMetaDoc =
1185 128 : CPLString(static_cast<const char *>(v_r), v_num);
1186 : }
1187 129 : psTree = CPLParseXMLString(osMetaDoc);
1188 : }
1189 :
1190 258 : if (bReload && psTree == nullptr &&
1191 258 : vfs.is_file(psPam->pszPamFilename))
1192 : {
1193 1 : auto nBytes = vfs.file_size(psPam->pszPamFilename);
1194 2 : tiledb::VFS::filebuf fbuf(vfs);
1195 1 : fbuf.open(psPam->pszPamFilename, std::ios::in);
1196 1 : std::istream is(&fbuf);
1197 1 : osMetaDoc.resize(nBytes);
1198 1 : is.read(&osMetaDoc[0], nBytes);
1199 1 : fbuf.close();
1200 1 : psTree = CPLParseXMLString(osMetaDoc);
1201 : }
1202 :
1203 257 : if (!bReload)
1204 : {
1205 128 : psTree = CPLParseXMLString(osMetaDoc);
1206 : }
1207 : }
1208 257 : CPLErrorReset();
1209 :
1210 257 : if (eLastErr != CE_None)
1211 0 : CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
1212 :
1213 : /* --------------------------------------------------------------------
1214 : */
1215 : /* If we are looking for a subdataset, search for its subtree not.
1216 : */
1217 : /* --------------------------------------------------------------------
1218 : */
1219 257 : if (psTree && !psPam->osSubdatasetName.empty())
1220 : {
1221 11 : CPLXMLNode *psSubTree = psTree->psChild;
1222 :
1223 65 : for (; psSubTree != nullptr; psSubTree = psSubTree->psNext)
1224 : {
1225 64 : if (psSubTree->eType != CXT_Element ||
1226 64 : !EQUAL(psSubTree->pszValue, "Subdataset"))
1227 38 : continue;
1228 :
1229 26 : if (!EQUAL(CPLGetXMLValue(psSubTree, "name", ""),
1230 : psPam->osSubdatasetName))
1231 16 : continue;
1232 :
1233 10 : psSubTree = CPLGetXMLNode(psSubTree, "PAMDataset");
1234 10 : break;
1235 : }
1236 :
1237 11 : if (psSubTree != nullptr)
1238 10 : psSubTree = CPLCloneXMLTree(psSubTree);
1239 :
1240 11 : CPLDestroyXMLNode(psTree);
1241 11 : psTree = psSubTree;
1242 : }
1243 257 : if (psTree == nullptr)
1244 1 : return CE_Failure;
1245 :
1246 256 : m_nOverviewCountFromMetadata =
1247 256 : atoi(CPLGetXMLValue(psTree, OVERVIEW_COUNT_KEY, "0"));
1248 256 : if (m_nOverviewCountFromMetadata)
1249 : {
1250 10 : CPLDebugOnly("TileDB", "OverviewCount = %d",
1251 : m_nOverviewCountFromMetadata);
1252 : }
1253 :
1254 : /* --------------------------------------------------------------------
1255 : */
1256 : /* Initialize ourselves from this XML tree. */
1257 : /* --------------------------------------------------------------------
1258 : */
1259 :
1260 256 : CPLString osPath(CPLGetPathSafe(psPam->pszPamFilename));
1261 256 : const CPLErr eErr = XMLInit(psTree, osPath);
1262 :
1263 256 : CPLDestroyXMLNode(psTree);
1264 :
1265 256 : if (eErr != CE_None)
1266 0 : PamClear();
1267 :
1268 256 : return eErr;
1269 : }
1270 0 : catch (const tiledb::TileDBError &e)
1271 : {
1272 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1273 0 : if (psTree)
1274 0 : CPLDestroyXMLNode(psTree);
1275 0 : return CE_Failure;
1276 : }
1277 : }
1278 :
1279 : /************************************************************************/
1280 : /* GetMetadata() */
1281 : /************************************************************************/
1282 :
1283 216 : CSLConstList TileDBRasterDataset::GetMetadata(const char *pszDomain)
1284 :
1285 : {
1286 216 : if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_SUBDATASETS))
1287 : {
1288 6 : if (m_aosSubdatasetMD.empty())
1289 : {
1290 5 : CSLConstList papszMD = GDALPamDataset::GetMetadata(pszDomain);
1291 8 : for (const auto &[pszKey, pszValue] :
1292 13 : cpl::IterateNameValue(papszMD))
1293 : {
1294 4 : if (STARTS_WITH(pszKey, "SUBDATASET_") &&
1295 4 : EQUAL(pszKey + strlen(pszKey) - strlen("_NAME"), "_NAME") &&
1296 2 : !STARTS_WITH(pszValue, "TILEDB:"))
1297 : {
1298 2 : m_aosSubdatasetMD.AddNameValue(
1299 2 : pszKey, CPLString().Printf("TILEDB:\"%s\":%s",
1300 2 : GetDescription(), pszValue));
1301 : }
1302 : else
1303 : {
1304 2 : m_aosSubdatasetMD.AddNameValue(pszKey, pszValue);
1305 : }
1306 : }
1307 : }
1308 6 : return m_aosSubdatasetMD.List();
1309 : }
1310 : else
1311 : {
1312 210 : return GDALPamDataset::GetMetadata(pszDomain);
1313 : }
1314 : }
1315 :
1316 : /************************************************************************/
1317 : /* Open() */
1318 : /************************************************************************/
1319 :
1320 129 : GDALDataset *TileDBRasterDataset::Open(GDALOpenInfo *poOpenInfo,
1321 : tiledb::Object::Type objectType)
1322 :
1323 : {
1324 : try
1325 : {
1326 129 : return OpenInternal(poOpenInfo, objectType);
1327 : }
1328 0 : catch (const tiledb::TileDBError &e)
1329 : {
1330 0 : CPLError(CE_Failure, CPLE_AppDefined, "TileDB: Open(%s) failed: %s",
1331 0 : poOpenInfo->pszFilename, e.what());
1332 0 : return nullptr;
1333 : }
1334 : }
1335 :
1336 : /************************************************************************/
1337 : /* OpenInternal() */
1338 : /************************************************************************/
1339 :
1340 129 : GDALDataset *TileDBRasterDataset::OpenInternal(GDALOpenInfo *poOpenInfo,
1341 : tiledb::Object::Type objectType)
1342 :
1343 : {
1344 258 : auto poDS = std::make_unique<TileDBRasterDataset>();
1345 :
1346 129 : poDS->m_bDeferredCreateHasRun = true;
1347 129 : poDS->m_bDeferredCreateHasBeenSuccessful = true;
1348 :
1349 : const char *pszConfig =
1350 129 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
1351 :
1352 : const char *pszTimestamp =
1353 129 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_TIMESTAMP");
1354 :
1355 129 : poDS->bStats = CPL_TO_BOOL(
1356 129 : CSLFetchBoolean(poOpenInfo->papszOpenOptions, "STATS", FALSE));
1357 :
1358 129 : if (pszConfig != nullptr)
1359 : {
1360 0 : poDS->m_osConfigFilename = pszConfig;
1361 0 : tiledb::Config cfg(pszConfig);
1362 0 : poDS->m_ctx.reset(new tiledb::Context(cfg));
1363 : }
1364 : else
1365 : {
1366 129 : poDS->m_ctx.reset(new tiledb::Context());
1367 : }
1368 129 : if (pszTimestamp)
1369 18 : poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
1370 :
1371 258 : CPLString osURI;
1372 258 : CPLString osSubdataset;
1373 :
1374 258 : std::string osAttrNameTmp;
1375 : const char *pszAttr =
1376 129 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_ATTRIBUTE");
1377 :
1378 129 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
1379 2 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
1380 : {
1381 : // form required read attributes and open file
1382 : // Create a corresponding GDALDataset.
1383 : CPLStringList apszName(
1384 2 : CSLTokenizeString2(poOpenInfo->pszFilename, ":",
1385 2 : CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
1386 :
1387 2 : if (apszName.size() != 3)
1388 : {
1389 0 : return nullptr;
1390 : }
1391 :
1392 2 : osURI = TileDBDataset::VSI_to_tiledb_uri(apszName[1]);
1393 2 : osSubdataset = apszName[2];
1394 4 : poDS->SetSubdatasetName(osSubdataset.c_str());
1395 : }
1396 : else
1397 : {
1398 127 : if (pszAttr != nullptr)
1399 : {
1400 4 : poDS->SetSubdatasetName(pszAttr);
1401 : }
1402 :
1403 127 : osURI = TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
1404 : }
1405 :
1406 258 : CPLString osAux = CPLGetBasenameSafe(osURI);
1407 129 : osAux += ".tdb";
1408 :
1409 : // aux file is in array folder
1410 258 : poDS->SetPhysicalFilename(
1411 258 : CPLFormFilenameSafe(osURI, osAux, nullptr).c_str());
1412 : // Initialize any PAM information.
1413 129 : poDS->SetDescription(osURI);
1414 :
1415 129 : poDS->m_osArrayURI = osURI;
1416 129 : if (objectType == tiledb::Object::Type::Group)
1417 : {
1418 116 : poDS->m_bDatasetInGroup = true;
1419 :
1420 : // Full resolution raster array
1421 116 : poDS->m_osArrayURI = CPLFormFilenameSafe(osURI.c_str(), "l_0", nullptr);
1422 : }
1423 :
1424 235 : if ((poOpenInfo->eAccess == GA_ReadOnly) &&
1425 106 : (!TileDBDataset::TileDBObjectExists(poDS->m_osArrayURI)))
1426 : {
1427 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1428 : "Failed to open %s as an array or group.",
1429 : poOpenInfo->pszFilename);
1430 0 : return nullptr;
1431 : }
1432 :
1433 129 : const tiledb_query_type_t eMode =
1434 129 : poOpenInfo->eAccess == GA_Update ? TILEDB_WRITE : TILEDB_READ;
1435 :
1436 129 : if (poDS->nTimestamp)
1437 : {
1438 36 : poDS->m_array = std::make_unique<tiledb::Array>(
1439 18 : *poDS->m_ctx, poDS->m_osArrayURI, eMode,
1440 54 : tiledb::TemporalPolicy(tiledb::TimeTravel, poDS->nTimestamp));
1441 : }
1442 : else
1443 : {
1444 222 : poDS->m_array = std::make_unique<tiledb::Array>(
1445 222 : *poDS->m_ctx, poDS->m_osArrayURI, eMode);
1446 : }
1447 :
1448 129 : poDS->eAccess = poOpenInfo->eAccess;
1449 :
1450 : // Force opening read-only dataset
1451 129 : if (eMode == TILEDB_WRITE)
1452 : {
1453 23 : tiledb::Context *ctx = nullptr;
1454 23 : poDS->GetArray(false, ctx);
1455 : }
1456 :
1457 : // dependent on PAM metadata for information about array
1458 129 : poDS->TryLoadXML();
1459 :
1460 258 : tiledb::ArraySchema schema = poDS->m_array->schema();
1461 :
1462 129 : CSLConstList papszStructMeta = poDS->GetMetadata(GDAL_MDD_IMAGE_STRUCTURE);
1463 129 : const char *pszXSize = CSLFetchNameValue(papszStructMeta, "X_SIZE");
1464 129 : if (pszXSize)
1465 : {
1466 113 : poDS->nRasterXSize = atoi(pszXSize);
1467 113 : if (poDS->nRasterXSize <= 0)
1468 : {
1469 0 : CPLError(CE_Failure, CPLE_AppDefined,
1470 : "Width %i should be greater than zero.",
1471 0 : poDS->nRasterXSize);
1472 0 : return nullptr;
1473 : }
1474 : }
1475 :
1476 129 : const char *pszYSize = CSLFetchNameValue(papszStructMeta, "Y_SIZE");
1477 129 : if (pszYSize)
1478 : {
1479 113 : poDS->nRasterYSize = atoi(pszYSize);
1480 113 : if (poDS->nRasterYSize <= 0)
1481 : {
1482 0 : CPLError(CE_Failure, CPLE_AppDefined,
1483 : "Height %i should be greater than zero.",
1484 0 : poDS->nRasterYSize);
1485 0 : return nullptr;
1486 : }
1487 : }
1488 :
1489 129 : const char *pszNBits = CSLFetchNameValue(papszStructMeta, GDALMD_NBITS);
1490 129 : if (pszNBits)
1491 : {
1492 113 : poDS->nBitsPerSample = atoi(pszNBits);
1493 : }
1494 :
1495 129 : const char *pszDataType = CSLFetchNameValue(papszStructMeta, "DATA_TYPE");
1496 129 : if (pszDataType)
1497 : {
1498 : // handle the case where arrays have been written with int type
1499 : // (2.5.0)
1500 113 : GDALDataType eDT = GDALGetDataTypeByName(pszDataType);
1501 113 : if (eDT == GDT_Unknown)
1502 : {
1503 1 : int t = atoi(pszDataType);
1504 1 : if ((t > 0) && (t < GDT_TypeCount))
1505 1 : poDS->eDataType = static_cast<GDALDataType>(atoi(pszDataType));
1506 : else
1507 : {
1508 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown data type %s.",
1509 : pszDataType);
1510 0 : return nullptr;
1511 : }
1512 : }
1513 : else
1514 : {
1515 112 : poDS->eDataType = eDT;
1516 : }
1517 : }
1518 : else
1519 : {
1520 16 : if (!pszAttr)
1521 : {
1522 16 : if (schema.attribute_num() == 1)
1523 : {
1524 14 : osAttrNameTmp = schema.attribute(0).name();
1525 14 : pszAttr = osAttrNameTmp.c_str();
1526 : }
1527 : }
1528 : }
1529 :
1530 : const char *pszIndexMode =
1531 129 : CSLFetchNameValue(papszStructMeta, GDALMD_INTERLEAVE);
1532 :
1533 129 : if (pszIndexMode)
1534 107 : option_to_index_type(pszIndexMode, poDS->eIndexMode);
1535 :
1536 258 : std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
1537 :
1538 129 : int iYDim = 0;
1539 129 : int iXDim = 1;
1540 129 : if ((dims.size() == 2) || (dims.size() == 3))
1541 : {
1542 129 : if (dims.size() == 3)
1543 : {
1544 141 : if ((pszAttr != nullptr) &&
1545 141 : (schema.attributes().count(pszAttr) == 0))
1546 : {
1547 0 : CPLError(CE_Failure, CPLE_NotSupported,
1548 : "%s attribute is not found in TileDB schema.",
1549 : pszAttr);
1550 0 : return nullptr;
1551 : }
1552 :
1553 123 : if (poDS->eIndexMode == PIXEL)
1554 15 : std::rotate(dims.begin(), dims.begin() + 2, dims.end());
1555 :
1556 123 : if (dims[0].type() != TILEDB_UINT64)
1557 : {
1558 0 : const char *pszTypeName = "";
1559 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1560 0 : CPLError(CE_Failure, CPLE_NotSupported,
1561 : "Unsupported BAND dimension type: %s", pszTypeName);
1562 0 : return nullptr;
1563 : }
1564 123 : poDS->nBandStart = dims[0].domain<uint64_t>().first;
1565 123 : const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
1566 246 : if (nBandEnd < poDS->nBandStart ||
1567 123 : nBandEnd - poDS->nBandStart >
1568 123 : static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1569 : {
1570 0 : CPLError(CE_Failure, CPLE_NotSupported,
1571 : "Invalid bounds for BAND dimension.");
1572 0 : return nullptr;
1573 : }
1574 123 : poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
1575 123 : iYDim = 1;
1576 123 : iXDim = 2;
1577 : }
1578 : else
1579 : {
1580 : const char *pszBands =
1581 6 : poDS->GetMetadataItem("NUM_BANDS", GDAL_MDD_IMAGE_STRUCTURE);
1582 6 : if (pszBands)
1583 : {
1584 1 : poDS->nBands = atoi(pszBands);
1585 : }
1586 :
1587 6 : poDS->eIndexMode = ATTRIBUTES;
1588 : }
1589 : }
1590 : else
1591 : {
1592 0 : CPLError(CE_Failure, CPLE_AppDefined,
1593 : "Wrong number of dimensions %d: expected 2 or 3.",
1594 0 : static_cast<int>(dims.size()));
1595 0 : return nullptr;
1596 : }
1597 :
1598 258 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
1599 129 : !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
1600 : {
1601 0 : return nullptr;
1602 : }
1603 :
1604 129 : if (dims[iYDim].type() != TILEDB_UINT64)
1605 : {
1606 0 : const char *pszTypeName = "";
1607 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1608 0 : CPLError(CE_Failure, CPLE_NotSupported,
1609 : "Unsupported Y dimension type: %s", pszTypeName);
1610 0 : return nullptr;
1611 : }
1612 129 : if (!pszYSize)
1613 : {
1614 16 : const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
1615 16 : const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
1616 32 : if (nStart != 0 ||
1617 16 : nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1618 : {
1619 0 : CPLError(CE_Failure, CPLE_NotSupported,
1620 : "Invalid bounds for Y dimension.");
1621 0 : return nullptr;
1622 : }
1623 16 : poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
1624 : }
1625 129 : const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
1626 129 : if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
1627 : {
1628 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
1629 0 : return nullptr;
1630 : }
1631 129 : poDS->nBlockYSize = static_cast<int>(nBlockYSize);
1632 :
1633 129 : if (dims[iXDim].type() != TILEDB_UINT64)
1634 : {
1635 0 : const char *pszTypeName = "";
1636 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1637 0 : CPLError(CE_Failure, CPLE_NotSupported,
1638 : "Unsupported Y dimension type: %s", pszTypeName);
1639 0 : return nullptr;
1640 : }
1641 129 : if (!pszXSize)
1642 : {
1643 16 : const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
1644 16 : const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
1645 32 : if (nStart != 0 ||
1646 16 : nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1647 : {
1648 0 : CPLError(CE_Failure, CPLE_NotSupported,
1649 : "Invalid bounds for X dimension.");
1650 0 : return nullptr;
1651 : }
1652 16 : poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
1653 : }
1654 129 : const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
1655 129 : if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
1656 : {
1657 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
1658 0 : return nullptr;
1659 : }
1660 129 : poDS->nBlockXSize = static_cast<int>(nBlockXSize);
1661 :
1662 129 : poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
1663 129 : poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
1664 :
1665 129 : if (dims.size() == 3)
1666 : {
1667 : // Create band information objects.
1668 844 : for (int i = 1; i <= poDS->nBands; ++i)
1669 : {
1670 721 : if (pszAttr == nullptr)
1671 189 : poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
1672 : else
1673 1064 : poDS->SetBand(
1674 1064 : i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
1675 : }
1676 : }
1677 : else // subdatasets or only attributes
1678 : {
1679 7 : if ((poOpenInfo->eAccess == GA_Update) &&
1680 1 : (poDS->GetMetadata(GDAL_MDD_SUBDATASETS) != nullptr))
1681 : {
1682 0 : CPLError(CE_Failure, CPLE_NotSupported,
1683 : "The TileDB driver does not support update access "
1684 : "to subdatasets.");
1685 0 : return nullptr;
1686 : }
1687 :
1688 6 : if (!osSubdataset.empty())
1689 : {
1690 : // do we have the attribute in the schema
1691 2 : if (schema.attributes().count(osSubdataset))
1692 : {
1693 0 : poDS->SetBand(
1694 0 : 1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
1695 : }
1696 : else
1697 : {
1698 2 : if (schema.attributes().count(osSubdataset + "_1"))
1699 : {
1700 : // Create band information objects.
1701 2 : for (int i = 1; i <= poDS->nBands; ++i)
1702 : {
1703 1 : CPLString osAttr = CPLString().Printf(
1704 2 : "%s_%d", osSubdataset.c_str(), i);
1705 2 : poDS->SetBand(
1706 1 : i, new TileDBRasterBand(poDS.get(), i, osAttr));
1707 : }
1708 : }
1709 : else
1710 : {
1711 1 : CPLError(CE_Failure, CPLE_NotSupported,
1712 : "%s attribute is not found in TileDB schema.",
1713 : osSubdataset.c_str());
1714 1 : return nullptr;
1715 : }
1716 : }
1717 : }
1718 : else
1719 : {
1720 4 : CSLConstList papszMeta = poDS->GetMetadata(GDAL_MDD_SUBDATASETS);
1721 4 : if (papszMeta != nullptr)
1722 : {
1723 1 : if ((CSLCount(papszMeta) / 2) == 1)
1724 : {
1725 : const char *pszSubDSName =
1726 0 : poDS->m_aosSubdatasetMD.FetchNameValueDef(
1727 : "SUBDATASET_1_NAME", "");
1728 0 : GDALOpenInfo oOpenInfo(pszSubDSName, poOpenInfo->eAccess);
1729 0 : return OpenInternal(&oOpenInfo, objectType);
1730 : }
1731 : }
1732 3 : else if (poDS->eIndexMode == ATTRIBUTES)
1733 : {
1734 3 : poDS->nBands = schema.attribute_num();
1735 :
1736 : // Create band information objects.
1737 11 : for (int i = 1; i <= poDS->nBands; ++i)
1738 : {
1739 16 : tiledb::Attribute const &attr = schema.attribute(i - 1);
1740 16 : std::string const &attr_name = attr.name();
1741 :
1742 8 : CPLDebug("TileDB", "Set attribute '%s' from TileDB schema",
1743 : attr_name.c_str());
1744 :
1745 : GDALRasterBand *band =
1746 8 : new TileDBRasterBand(poDS.get(), i, attr_name.c_str());
1747 :
1748 8 : poDS->SetBand(i, band);
1749 : }
1750 : }
1751 : else
1752 : {
1753 0 : CPLError(CE_Failure, CPLE_NotSupported,
1754 : "%s is missing required TileDB subdataset metadata.",
1755 : osURI.c_str());
1756 0 : return nullptr;
1757 : }
1758 : }
1759 : }
1760 :
1761 : // reload metadata now that bands are created to populate band metadata
1762 128 : poDS->TryLoadCachedXML(nullptr, false);
1763 :
1764 256 : tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
1765 :
1766 128 : if (!STARTS_WITH_CI(osURI, "TILEDB:") && vfs.is_dir(osURI))
1767 128 : poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
1768 : else
1769 0 : CPLError(CE_Warning, CPLE_AppDefined,
1770 : "Overviews not supported for network writes.");
1771 :
1772 128 : return poDS.release();
1773 : }
1774 :
1775 : /************************************************************************/
1776 : /* CreateAttribute() */
1777 : /************************************************************************/
1778 :
1779 : template <class T, class NoDataT = T>
1780 129 : static tiledb::Attribute CreateAttribute(tiledb::Context &ctx,
1781 : const std::string &osAttrName,
1782 : tiledb::FilterList &filterList,
1783 : bool bHasFillValue, double dfFillValue)
1784 : {
1785 129 : auto attr = tiledb::Attribute::create<T>(ctx, osAttrName, filterList);
1786 129 : if (bHasFillValue && GDALIsValueInRange<NoDataT>(dfFillValue))
1787 : {
1788 30 : const auto nVal = static_cast<NoDataT>(dfFillValue);
1789 30 : if (dfFillValue == static_cast<double>(nVal))
1790 : {
1791 : if constexpr (sizeof(T) == sizeof(NoDataT))
1792 : {
1793 26 : attr.set_fill_value(&nVal, sizeof(nVal));
1794 : }
1795 : else
1796 : {
1797 4 : T aVal = {nVal, nVal};
1798 4 : attr.set_fill_value(&aVal, sizeof(aVal));
1799 : }
1800 : }
1801 : }
1802 129 : return attr;
1803 : }
1804 :
1805 : /************************************************************************/
1806 : /* CreateAttribute() */
1807 : /************************************************************************/
1808 :
1809 123 : CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
1810 : const CPLString &osAttrName,
1811 : const int nSubRasterCount,
1812 : bool bHasFillValue,
1813 : double dfFillValue)
1814 : {
1815 : try
1816 : {
1817 252 : for (int i = 0; i < nSubRasterCount; ++i)
1818 : {
1819 129 : CPLString osName(osAttrName);
1820 : // a few special cases
1821 : // remove any leading slashes or
1822 : // additional slashes as in the case of hdf5
1823 129 : if STARTS_WITH (osName, "//")
1824 : {
1825 2 : osName = osName.substr(2);
1826 : }
1827 :
1828 129 : osName.replaceAll("/", "_");
1829 129 : CPLString osPrettyName = osName;
1830 :
1831 129 : if ((eIndexMode == ATTRIBUTES) ||
1832 115 : ((m_bHasSubDatasets) && (nSubRasterCount > 1)))
1833 : {
1834 14 : osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
1835 : }
1836 :
1837 129 : switch (eType)
1838 : {
1839 53 : case GDT_UInt8:
1840 : {
1841 159 : m_schema->add_attribute(::CreateAttribute<unsigned char>(
1842 53 : *m_ctx, osName, *m_filterList, bHasFillValue,
1843 106 : dfFillValue));
1844 53 : nBitsPerSample = 8;
1845 53 : break;
1846 : }
1847 4 : case GDT_Int8:
1848 : {
1849 4 : m_schema->add_attribute(
1850 8 : ::CreateAttribute<int8_t>(*m_ctx, osName, *m_filterList,
1851 8 : bHasFillValue, dfFillValue));
1852 4 : nBitsPerSample = 8;
1853 4 : break;
1854 : }
1855 5 : case GDT_UInt16:
1856 : {
1857 15 : m_schema->add_attribute(::CreateAttribute<uint16_t>(
1858 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1859 10 : dfFillValue));
1860 5 : nBitsPerSample = 16;
1861 5 : break;
1862 : }
1863 5 : case GDT_UInt32:
1864 : {
1865 15 : m_schema->add_attribute(::CreateAttribute<uint32_t>(
1866 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1867 10 : dfFillValue));
1868 5 : nBitsPerSample = 32;
1869 5 : break;
1870 : }
1871 4 : case GDT_UInt64:
1872 : {
1873 12 : m_schema->add_attribute(::CreateAttribute<uint64_t>(
1874 4 : *m_ctx, osName, *m_filterList, bHasFillValue,
1875 8 : dfFillValue));
1876 4 : nBitsPerSample = 64;
1877 4 : break;
1878 : }
1879 5 : case GDT_Int16:
1880 : {
1881 15 : m_schema->add_attribute(::CreateAttribute<int16_t>(
1882 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1883 10 : dfFillValue));
1884 5 : nBitsPerSample = 16;
1885 5 : break;
1886 : }
1887 7 : case GDT_Int32:
1888 : {
1889 21 : m_schema->add_attribute(::CreateAttribute<int32_t>(
1890 7 : *m_ctx, osName, *m_filterList, bHasFillValue,
1891 14 : dfFillValue));
1892 7 : nBitsPerSample = 32;
1893 7 : break;
1894 : }
1895 4 : case GDT_Int64:
1896 : {
1897 12 : m_schema->add_attribute(::CreateAttribute<int64_t>(
1898 4 : *m_ctx, osName, *m_filterList, bHasFillValue,
1899 8 : dfFillValue));
1900 4 : nBitsPerSample = 64;
1901 4 : break;
1902 : }
1903 12 : case GDT_Float32:
1904 : {
1905 12 : m_schema->add_attribute(
1906 24 : ::CreateAttribute<float>(*m_ctx, osName, *m_filterList,
1907 24 : bHasFillValue, dfFillValue));
1908 12 : nBitsPerSample = 32;
1909 12 : break;
1910 : }
1911 8 : case GDT_Float64:
1912 : {
1913 8 : m_schema->add_attribute(
1914 16 : ::CreateAttribute<double>(*m_ctx, osName, *m_filterList,
1915 16 : bHasFillValue, dfFillValue));
1916 8 : nBitsPerSample = 64;
1917 8 : break;
1918 : }
1919 5 : case GDT_CInt16:
1920 : {
1921 5 : m_schema->add_attribute(
1922 10 : ::CreateAttribute<int16_t[2], int16_t>(
1923 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1924 10 : dfFillValue));
1925 5 : nBitsPerSample = 16;
1926 5 : break;
1927 : }
1928 5 : case GDT_CInt32:
1929 : {
1930 5 : m_schema->add_attribute(
1931 10 : ::CreateAttribute<int32_t[2], int32_t>(
1932 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1933 10 : dfFillValue));
1934 5 : nBitsPerSample = 32;
1935 5 : break;
1936 : }
1937 5 : case GDT_CFloat32:
1938 : {
1939 15 : m_schema->add_attribute(::CreateAttribute<float[2], float>(
1940 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1941 10 : dfFillValue));
1942 5 : nBitsPerSample = 32;
1943 5 : break;
1944 : }
1945 7 : case GDT_CFloat64:
1946 : {
1947 7 : m_schema->add_attribute(
1948 14 : ::CreateAttribute<double[2], double>(
1949 7 : *m_ctx, osName, *m_filterList, bHasFillValue,
1950 14 : dfFillValue));
1951 7 : nBitsPerSample = 64;
1952 7 : break;
1953 : }
1954 0 : default:
1955 0 : return CE_Failure;
1956 : }
1957 :
1958 129 : if ((m_bHasSubDatasets) && (i == 0))
1959 : {
1960 8 : CPLString osDim;
1961 8 : switch (nSubRasterCount)
1962 : {
1963 0 : case 2:
1964 0 : osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
1965 0 : break;
1966 8 : default:
1967 : osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
1968 8 : nRasterYSize);
1969 8 : break;
1970 : }
1971 :
1972 8 : const int nSubDataCount = 1 + m_aosSubdatasetMD.size() / 2;
1973 : m_aosSubdatasetMD.SetNameValue(
1974 16 : CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
1975 24 : CPLString().Printf("%s", osPrettyName.c_str()));
1976 :
1977 : m_aosSubdatasetMD.SetNameValue(
1978 16 : CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
1979 8 : CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
1980 : osPrettyName.c_str(),
1981 16 : GDALGetDataTypeName(eType)));
1982 :
1983 : // add to PAM metadata
1984 8 : if (!m_poSubDatasetsTree)
1985 : {
1986 3 : m_poSubDatasetsTree.reset(
1987 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
1988 : }
1989 :
1990 8 : CPLXMLNode *psSubNode = CPLCreateXMLNode(
1991 : m_poSubDatasetsTree.get(), CXT_Element, "Subdataset");
1992 8 : CPLAddXMLAttributeAndValue(psSubNode, "name",
1993 : osPrettyName.c_str());
1994 :
1995 8 : CPLXMLNode *psMetaNode = CPLCreateXMLNode(
1996 : CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
1997 : CXT_Element, "Metadata");
1998 8 : CPLAddXMLAttributeAndValue(psMetaNode, "domain",
1999 : GDAL_MDD_IMAGE_STRUCTURE);
2000 :
2001 8 : CPLAddXMLAttributeAndValue(
2002 : CPLCreateXMLElementAndValue(
2003 : psMetaNode, "MDI",
2004 16 : CPLString().Printf("%d", nRasterXSize)),
2005 : "KEY", "X_SIZE");
2006 :
2007 8 : CPLAddXMLAttributeAndValue(
2008 : CPLCreateXMLElementAndValue(
2009 : psMetaNode, "MDI",
2010 16 : CPLString().Printf("%d", nRasterYSize)),
2011 : "KEY", "Y_SIZE");
2012 :
2013 8 : CPLAddXMLAttributeAndValue(
2014 : CPLCreateXMLElementAndValue(
2015 : psMetaNode, "MDI",
2016 16 : CPLString().Printf("%s", GDALGetDataTypeName(eType))),
2017 : "KEY", "DATA_TYPE");
2018 :
2019 8 : if (m_lpoAttributeDS.size() > 0)
2020 : {
2021 6 : CPLAddXMLAttributeAndValue(
2022 : CPLCreateXMLElementAndValue(
2023 : psMetaNode, "MDI",
2024 12 : CPLString().Printf("%d", nBands)),
2025 : "KEY", "NUM_BANDS");
2026 : }
2027 : else
2028 : {
2029 2 : CPLAddXMLAttributeAndValue(
2030 : CPLCreateXMLElementAndValue(
2031 : psMetaNode, "MDI",
2032 4 : CPLString().Printf("%d", nSubRasterCount)),
2033 : "KEY", "NUM_BANDS");
2034 : }
2035 :
2036 8 : CPLAddXMLAttributeAndValue(
2037 : CPLCreateXMLElementAndValue(
2038 : psMetaNode, "MDI",
2039 16 : CPLString().Printf("%d", nBitsPerSample)),
2040 : "KEY", GDALMD_NBITS);
2041 : }
2042 : }
2043 123 : return CE_None;
2044 : }
2045 0 : catch (const tiledb::TileDBError &e)
2046 : {
2047 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2048 0 : return CE_Failure;
2049 : }
2050 : }
2051 :
2052 : /************************************************************************/
2053 : /* SetBlockSize() */
2054 : /************************************************************************/
2055 :
2056 1 : void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
2057 : CPLStringList &aosOptions)
2058 :
2059 : {
2060 1 : int nX = 0;
2061 1 : int nY = 0;
2062 1 : poBand->GetBlockSize(&nX, &nY);
2063 :
2064 1 : if (!aosOptions.FetchNameValue("BLOCKXSIZE"))
2065 : {
2066 1 : aosOptions.SetNameValue("BLOCKXSIZE", CPLString().Printf("%d", nX));
2067 : }
2068 :
2069 1 : if (!aosOptions.FetchNameValue("BLOCKYSIZE"))
2070 : {
2071 1 : aosOptions.SetNameValue("BLOCKYSIZE", CPLString().Printf("%d", nY));
2072 : }
2073 1 : }
2074 :
2075 : /************************************************************************/
2076 : /* CreateLL() */
2077 : /* */
2078 : /* Shared functionality between TileDBDataset::Create() and */
2079 : /* TileDBDataset::CreateCopy() for creating TileDB array based on */
2080 : /* a set of options and a configuration. */
2081 : /************************************************************************/
2082 :
2083 120 : TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
2084 : int nXSize, int nYSize,
2085 : int nBandsIn,
2086 : GDALDataType eType,
2087 : CSLConstList papszOptions)
2088 : {
2089 : try
2090 : {
2091 120 : if ((nXSize <= 0 && nYSize <= 0))
2092 : {
2093 0 : return nullptr;
2094 : }
2095 :
2096 240 : auto poDS = std::make_unique<TileDBRasterDataset>();
2097 120 : poDS->nRasterXSize = nXSize;
2098 120 : poDS->nRasterYSize = nYSize;
2099 120 : poDS->eDataType = eType;
2100 120 : poDS->nBands = nBandsIn;
2101 120 : poDS->eAccess = GA_Update;
2102 :
2103 120 : if (poDS->nBands == 0) // subdatasets
2104 : {
2105 1 : poDS->eIndexMode = ATTRIBUTES;
2106 : }
2107 : else
2108 : {
2109 : const char *pszIndexMode =
2110 119 : CSLFetchNameValue(papszOptions, GDALMD_INTERLEAVE);
2111 :
2112 119 : if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
2113 0 : return nullptr;
2114 : }
2115 :
2116 : const char *pszConfig =
2117 120 : CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
2118 120 : if (pszConfig != nullptr)
2119 : {
2120 0 : poDS->m_osConfigFilename = pszConfig;
2121 0 : tiledb::Config cfg(pszConfig);
2122 0 : poDS->m_ctx.reset(new tiledb::Context(cfg));
2123 : }
2124 : else
2125 : {
2126 120 : poDS->m_ctx.reset(new tiledb::Context());
2127 : }
2128 :
2129 120 : if (CPLTestBool(
2130 : CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "YES")))
2131 : {
2132 110 : poDS->m_bDatasetInGroup = true;
2133 116 : tiledb::create_group(*(poDS->m_ctx.get()), pszFilename);
2134 :
2135 : {
2136 107 : tiledb::Group group(*(poDS->m_ctx.get()), pszFilename,
2137 428 : TILEDB_WRITE);
2138 107 : group.put_metadata(
2139 : DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
2140 : static_cast<int>(strlen(RASTER_DATASET_TYPE)),
2141 : RASTER_DATASET_TYPE);
2142 :
2143 107 : group.close();
2144 : }
2145 :
2146 : // Full resolution raster array
2147 107 : poDS->m_osArrayURI =
2148 214 : CPLFormFilenameSafe(pszFilename, "l_0", nullptr);
2149 : }
2150 : else
2151 : {
2152 10 : poDS->m_osArrayURI = pszFilename;
2153 : }
2154 :
2155 : const char *pszCompression =
2156 117 : CSLFetchNameValue(papszOptions, GDALMD_COMPRESSION);
2157 : const char *pszCompressionLevel =
2158 117 : CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
2159 :
2160 : const char *pszBlockXSize =
2161 117 : CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
2162 117 : poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
2163 : const char *pszBlockYSize =
2164 117 : CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
2165 117 : poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
2166 117 : poDS->bStats =
2167 117 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "STATS", FALSE));
2168 :
2169 : const char *pszTimestamp =
2170 117 : CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
2171 117 : if (pszTimestamp != nullptr)
2172 2 : poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
2173 :
2174 : // set dimensions and attribute type for schema
2175 234 : poDS->m_schema.reset(
2176 117 : new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
2177 117 : poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
2178 117 : poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
2179 :
2180 117 : poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
2181 :
2182 117 : if (pszCompression != nullptr)
2183 : {
2184 0 : int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
2185 0 : if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
2186 0 : *(poDS->m_filterList.get()),
2187 0 : pszCompression, nLevel) == CE_None)
2188 : {
2189 0 : poDS->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
2190 0 : GDAL_MDD_IMAGE_STRUCTURE);
2191 0 : poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
2192 : }
2193 : }
2194 :
2195 234 : CPLString osAux = CPLGetBasenameSafe(pszFilename);
2196 117 : osAux += ".tdb";
2197 :
2198 234 : poDS->SetPhysicalFilename(
2199 234 : CPLFormFilenameSafe(pszFilename, osAux.c_str(), nullptr).c_str());
2200 :
2201 : // Initialize PAM information.
2202 117 : poDS->SetDescription(pszFilename);
2203 :
2204 : // Note the dimension bounds are inclusive and are expanded to the match
2205 : // the block size
2206 117 : poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
2207 117 : poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
2208 :
2209 : // register additional attributes to the pixel value, these will be
2210 : // be reported as subdatasets on future reads
2211 : CSLConstList papszAttributes =
2212 117 : CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
2213 121 : for (const char *pszAttribute : cpl::Iterate(papszAttributes))
2214 : {
2215 : // modeling additional attributes as subdatasets
2216 4 : poDS->m_bHasSubDatasets = true;
2217 : // check each attribute is a GDAL source
2218 : std::unique_ptr<GDALDataset> poAttrDS(
2219 8 : GDALDataset::Open(pszAttribute, GA_ReadOnly));
2220 :
2221 4 : if (poAttrDS != nullptr)
2222 : {
2223 : // check each is co-registered
2224 : // candidate band
2225 4 : int nAttrBands = poAttrDS->GetRasterCount();
2226 4 : if (nAttrBands > 0)
2227 : {
2228 4 : GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
2229 :
2230 4 : if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
2231 8 : (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
2232 4 : (poDS->nBands == nAttrBands))
2233 : {
2234 : // could check geotransform, but it is sufficient
2235 : // that cartesian dimensions are equal
2236 4 : poDS->m_lpoAttributeDS.push_back(std::move(poAttrDS));
2237 : }
2238 : else
2239 : {
2240 0 : CPLError(
2241 : CE_Warning, CPLE_AppDefined,
2242 : "Skipping %s as it has a different dimension\n",
2243 : pszAttribute);
2244 : }
2245 : }
2246 : else
2247 : {
2248 0 : CPLError(CE_Warning, CPLE_AppDefined,
2249 : "Skipping %s as it doesn't have any bands\n",
2250 : pszAttribute);
2251 : }
2252 : }
2253 : else
2254 : {
2255 0 : CPLError(CE_Warning, CPLE_AppDefined,
2256 : "Skipping %s, not recognized as a GDAL dataset\n",
2257 : pszAttribute);
2258 : }
2259 : }
2260 :
2261 117 : return poDS.release();
2262 : }
2263 3 : catch (const tiledb::TileDBError &e)
2264 : {
2265 3 : CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
2266 3 : return nullptr;
2267 : }
2268 : }
2269 :
2270 : /************************************************************************/
2271 : /* DeferredCreate() */
2272 : /* */
2273 : /* Create dimension, domains and attributes. and optionally the array */
2274 : /************************************************************************/
2275 :
2276 120 : bool TileDBRasterDataset::DeferredCreate(bool bCreateArray)
2277 : {
2278 120 : CPLAssert(!m_bDeferredCreateHasRun);
2279 120 : m_bDeferredCreateHasRun = true;
2280 120 : m_bDeferredCreateHasBeenSuccessful = false;
2281 :
2282 : try
2283 : {
2284 : // this driver enforces that all subdatasets are the same size
2285 240 : tiledb::Domain domain(*m_ctx);
2286 :
2287 120 : const uint64_t w = static_cast<uint64_t>(nBlocksX) * nBlockXSize - 1;
2288 120 : const uint64_t h = static_cast<uint64_t>(nBlocksY) * nBlockYSize - 1;
2289 :
2290 120 : auto d1 = tiledb::Dimension::create<uint64_t>(*m_ctx, "X", {0, w},
2291 483 : uint64_t(nBlockXSize));
2292 117 : auto d2 = tiledb::Dimension::create<uint64_t>(*m_ctx, "Y", {0, h},
2293 468 : uint64_t(nBlockYSize));
2294 :
2295 : {
2296 : CPLErr eErr;
2297 : // Only used for unit test purposes (to check ability of GDAL to read
2298 : // an arbitrary array)
2299 : const char *pszAttrName =
2300 117 : CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
2301 117 : if ((nBands == 0) || (eIndexMode == ATTRIBUTES))
2302 : {
2303 6 : eErr = AddDimensions(domain, pszAttrName, d2, d1, nullptr);
2304 : }
2305 : else
2306 : {
2307 : auto d3 = tiledb::Dimension::create<uint64_t>(
2308 222 : *m_ctx, "BANDS", {1, uint64_t(nBands)}, 1);
2309 111 : eErr = AddDimensions(domain, pszAttrName, d2, d1, &d3);
2310 : }
2311 117 : if (eErr != CE_None)
2312 0 : return false;
2313 : }
2314 :
2315 117 : m_schema->set_domain(domain).set_order(
2316 117 : {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
2317 :
2318 : // register additional attributes to the pixel value, these will be
2319 : // be reported as subdatasets on future reads
2320 121 : for (const auto &poAttrDS : m_lpoAttributeDS)
2321 : {
2322 : const std::string osAttrName =
2323 4 : CPLGetBasenameSafe(poAttrDS->GetDescription());
2324 4 : GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
2325 4 : int bHasNoData = false;
2326 4 : const double dfNoData = poAttrBand->GetNoDataValue(&bHasNoData);
2327 4 : CreateAttribute(poAttrBand->GetRasterDataType(), osAttrName.c_str(),
2328 4 : 1, CPL_TO_BOOL(bHasNoData), dfNoData);
2329 : }
2330 :
2331 117 : if (bCreateArray)
2332 : {
2333 116 : CreateArray();
2334 : }
2335 :
2336 117 : m_bDeferredCreateHasBeenSuccessful = true;
2337 117 : return true;
2338 : }
2339 3 : catch (const tiledb::TileDBError &e)
2340 : {
2341 3 : CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
2342 3 : return false;
2343 : }
2344 : }
2345 :
2346 : /************************************************************************/
2347 : /* CreateArray() */
2348 : /************************************************************************/
2349 :
2350 117 : void TileDBRasterDataset::CreateArray()
2351 : {
2352 117 : tiledb::Array::create(m_osArrayURI, *m_schema);
2353 :
2354 117 : if (m_bDatasetInGroup)
2355 : {
2356 321 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
2357 107 : group.add_member(m_osArrayURI, false);
2358 107 : group.close();
2359 : }
2360 :
2361 117 : if (nTimestamp)
2362 4 : m_array.reset(new tiledb::Array(
2363 2 : *m_ctx, m_osArrayURI, TILEDB_WRITE,
2364 6 : tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
2365 : else
2366 115 : m_array.reset(new tiledb::Array(*m_ctx, m_osArrayURI, TILEDB_WRITE));
2367 117 : }
2368 :
2369 : /************************************************************************/
2370 : /* CopySubDatasets() */
2371 : /* */
2372 : /* Copy SubDatasets from src to a TileDBRasterDataset */
2373 : /* */
2374 : /************************************************************************/
2375 :
2376 1 : CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
2377 : TileDBRasterDataset *poDstDS,
2378 : GDALProgressFunc pfnProgress,
2379 : void *pProgressData)
2380 :
2381 : {
2382 : try
2383 : {
2384 2 : std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
2385 1 : poDstDS->m_bHasSubDatasets = true;
2386 : CSLConstList papszSrcSubDatasets =
2387 1 : poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
2388 1 : if (!papszSrcSubDatasets)
2389 0 : return CE_Failure;
2390 : const char *pszSubDSName =
2391 1 : CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
2392 1 : if (!pszSubDSName)
2393 0 : return CE_Failure;
2394 :
2395 : CPLStringList apszTokens(CSLTokenizeString2(
2396 2 : pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
2397 : // FIXME? this is tailored for HDF5-like subdataset names
2398 : // HDF5:foo.hdf5:attrname.
2399 1 : if (apszTokens.size() != 3)
2400 : {
2401 0 : CPLError(CE_Failure, CPLE_AppDefined,
2402 : "Cannot guess attribute name in %s", pszSubDSName);
2403 0 : return CE_Failure;
2404 : }
2405 :
2406 : std::unique_ptr<GDALDataset> poSubDataset(
2407 2 : GDALDataset::Open(pszSubDSName));
2408 2 : if (poSubDataset.get() == nullptr ||
2409 1 : poSubDataset->GetRasterCount() == 0)
2410 : {
2411 0 : return CE_Failure;
2412 : }
2413 :
2414 1 : uint64_t nSubXSize = poSubDataset->GetRasterXSize();
2415 1 : uint64_t nSubYSize = poSubDataset->GetRasterYSize();
2416 :
2417 1 : const char *pszAttrName = apszTokens[2];
2418 :
2419 1 : auto poFirstSubDSBand = poSubDataset->GetRasterBand(1);
2420 1 : int bFirstSubDSBandHasNoData = FALSE;
2421 : const double dfFirstSubDSBandNoData =
2422 1 : poFirstSubDSBand->GetNoDataValue(&bFirstSubDSBandHasNoData);
2423 1 : poDstDS->CreateAttribute(poFirstSubDSBand->GetRasterDataType(),
2424 : pszAttrName, poSubDataset->GetRasterCount(),
2425 1 : CPL_TO_BOOL(bFirstSubDSBandHasNoData),
2426 : dfFirstSubDSBandNoData);
2427 1 : apoDatasets.push_back(std::move(poSubDataset));
2428 :
2429 8 : for (const auto &[pszKey, pszValue] :
2430 9 : cpl::IterateNameValue(papszSrcSubDatasets))
2431 : {
2432 4 : if (EQUAL(pszKey, "SUBDATASET_1_NAME") || !strstr(pszKey, "_NAME"))
2433 : {
2434 3 : continue;
2435 : }
2436 1 : pszSubDSName = pszValue;
2437 : apszTokens = CSLTokenizeString2(
2438 1 : pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
2439 1 : if (apszTokens.size() != 3)
2440 : {
2441 0 : CPLError(CE_Failure, CPLE_AppDefined,
2442 : "Cannot guess attribute name in %s", pszSubDSName);
2443 0 : continue;
2444 : }
2445 :
2446 : std::unique_ptr<GDALDataset> poSubDS(
2447 2 : GDALDataset::Open(pszSubDSName));
2448 1 : if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
2449 : {
2450 1 : GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
2451 : int nBlockXSize, nBlockYSize;
2452 1 : poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
2453 :
2454 1 : int bHasNoData = FALSE;
2455 1 : const double dfNoData = poBand->GetNoDataValue(&bHasNoData);
2456 :
2457 1 : if ((poSubDS->GetRasterXSize() !=
2458 1 : static_cast<int>(nSubXSize)) ||
2459 1 : (poSubDS->GetRasterYSize() !=
2460 1 : static_cast<int>(nSubYSize)) ||
2461 3 : (nBlockXSize != poDstDS->nBlockXSize) ||
2462 1 : (nBlockYSize != poDstDS->nBlockYSize))
2463 : {
2464 0 : CPLError(CE_Warning, CPLE_AppDefined,
2465 : "Sub-datasets must have the same dimension,"
2466 : " and block sizes, skipping %s",
2467 : pszSubDSName);
2468 : }
2469 : else
2470 : {
2471 1 : pszAttrName = apszTokens[2];
2472 1 : poDstDS->CreateAttribute(
2473 : poSubDS->GetRasterBand(1)->GetRasterDataType(),
2474 : pszAttrName, poSubDS->GetRasterCount(),
2475 1 : CPL_TO_BOOL(bHasNoData), dfNoData);
2476 1 : apoDatasets.push_back(std::move(poSubDS));
2477 : }
2478 : }
2479 : else
2480 : {
2481 0 : CPLError(
2482 : CE_Warning, CPLE_AppDefined,
2483 : "Sub-datasets must be not null and contain data in bands,"
2484 : "skipping %s\n",
2485 : pszSubDSName);
2486 : }
2487 : }
2488 :
2489 1 : poDstDS->SetMetadata(poDstDS->m_aosSubdatasetMD.List(),
2490 1 : GDAL_MDD_SUBDATASETS);
2491 :
2492 1 : poDstDS->CreateArray();
2493 :
2494 : /* -------------------------------------------------------- */
2495 : /* Report preliminary (0) progress. */
2496 : /* --------------------------------------------------------- */
2497 1 : if (!pfnProgress(0.0, nullptr, pProgressData))
2498 : {
2499 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
2500 : "User terminated CreateCopy()");
2501 0 : return CE_Failure;
2502 : }
2503 :
2504 : // copy over subdatasets by block
2505 2 : tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
2506 1 : query.set_layout(TILEDB_GLOBAL_ORDER);
2507 1 : const uint64_t nTotalBlocks =
2508 1 : static_cast<uint64_t>(poDstDS->nBlocksX) * poDstDS->nBlocksY;
2509 1 : uint64_t nBlockCounter = 0;
2510 :
2511 : // row-major
2512 6 : for (int j = 0; j < poDstDS->nBlocksY; ++j)
2513 : {
2514 55 : for (int i = 0; i < poDstDS->nBlocksX; ++i)
2515 : {
2516 50 : std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
2517 : // have to write set all tiledb attributes on write
2518 50 : int iAttr = 0;
2519 150 : for (auto &poSubDS : apoDatasets)
2520 : {
2521 : const GDALDataType eDT =
2522 100 : poSubDS->GetRasterBand(1)->GetRasterDataType();
2523 :
2524 200 : for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
2525 : {
2526 100 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
2527 100 : const size_t nValues =
2528 100 : static_cast<size_t>(poDstDS->nBlockXSize) *
2529 100 : poDstDS->nBlockYSize;
2530 100 : void *pBlock = VSI_MALLOC_VERBOSE(nDTSize * nValues);
2531 100 : if (!pBlock)
2532 0 : return CE_Failure;
2533 100 : aBlocks.emplace_back(pBlock, &VSIFree);
2534 100 : GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
2535 100 : if (poBand->ReadBlock(i, j, pBlock) == CE_None)
2536 : {
2537 100 : SetBuffer(
2538 : &query, eDT,
2539 200 : poDstDS->m_schema->attribute(iAttr).name(),
2540 : pBlock, nValues);
2541 : }
2542 100 : ++iAttr;
2543 : }
2544 : }
2545 :
2546 50 : if (poDstDS->bStats)
2547 0 : tiledb::Stats::enable();
2548 :
2549 50 : auto status = query.submit();
2550 :
2551 50 : if (poDstDS->bStats)
2552 : {
2553 0 : tiledb::Stats::dump(stdout);
2554 0 : tiledb::Stats::disable();
2555 : }
2556 :
2557 50 : if (status == tiledb::Query::Status::FAILED)
2558 : {
2559 0 : return CE_Failure;
2560 : }
2561 :
2562 50 : ++nBlockCounter;
2563 50 : if (!pfnProgress(static_cast<double>(nBlockCounter) /
2564 50 : static_cast<double>(nTotalBlocks),
2565 : nullptr, pProgressData))
2566 : {
2567 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
2568 : "User terminated CreateCopy()");
2569 0 : return CE_Failure;
2570 : }
2571 : }
2572 : }
2573 :
2574 1 : query.finalize();
2575 :
2576 1 : return CE_None;
2577 : }
2578 0 : catch (const tiledb::TileDBError &e)
2579 : {
2580 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2581 0 : return CE_Failure;
2582 : }
2583 : }
2584 :
2585 : /************************************************************************/
2586 : /* Create() */
2587 : /************************************************************************/
2588 :
2589 119 : TileDBRasterDataset *TileDBRasterDataset::Create(const char *pszFilename,
2590 : int nXSize, int nYSize,
2591 : int nBandsIn,
2592 : GDALDataType eType,
2593 : CSLConstList papszOptions)
2594 :
2595 : {
2596 238 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
2597 :
2598 : std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
2599 238 : osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
2600 :
2601 119 : if (!poDS)
2602 3 : return nullptr;
2603 :
2604 : const char *pszAttrName =
2605 116 : CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
2606 314 : for (int i = 0; i < poDS->nBands; i++)
2607 : {
2608 198 : if (poDS->eIndexMode == ATTRIBUTES)
2609 24 : poDS->SetBand(
2610 : i + 1, new TileDBRasterBand(
2611 12 : poDS.get(), i + 1,
2612 24 : TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
2613 : else
2614 558 : poDS->SetBand(i + 1,
2615 372 : new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
2616 : }
2617 :
2618 : // TILEDB_WRITE_IMAGE_STRUCTURE=NO only used for unit test purposes (to
2619 : // check ability of GDAL to read an arbitrary array)
2620 116 : if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
2621 : {
2622 204 : CPLStringList aosImageStruct;
2623 : aosImageStruct.SetNameValue(
2624 102 : GDALMD_NBITS, CPLString().Printf("%d", poDS->nBitsPerSample));
2625 : aosImageStruct.SetNameValue(
2626 : "DATA_TYPE",
2627 102 : CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
2628 : aosImageStruct.SetNameValue(
2629 102 : "X_SIZE", CPLString().Printf("%d", poDS->nRasterXSize));
2630 : aosImageStruct.SetNameValue(
2631 102 : "Y_SIZE", CPLString().Printf("%d", poDS->nRasterYSize));
2632 : aosImageStruct.SetNameValue(GDALMD_INTERLEAVE,
2633 102 : index_type_name(poDS->eIndexMode));
2634 102 : aosImageStruct.SetNameValue("DATASET_TYPE", RASTER_DATASET_TYPE);
2635 :
2636 102 : if (poDS->m_lpoAttributeDS.size() > 0)
2637 : {
2638 2 : int i = 0;
2639 6 : for (auto const &poAttrDS : poDS->m_lpoAttributeDS)
2640 : {
2641 4 : aosImageStruct.SetNameValue(
2642 8 : CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
2643 12 : CPLGetBasenameSafe(poAttrDS->GetDescription()).c_str());
2644 : }
2645 : }
2646 102 : poDS->SetMetadata(aosImageStruct.List(), GDAL_MDD_IMAGE_STRUCTURE);
2647 : }
2648 :
2649 116 : return poDS.release();
2650 : }
2651 :
2652 : /************************************************************************/
2653 : /* CreateCopy() */
2654 : /************************************************************************/
2655 :
2656 52 : GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
2657 : GDALDataset *poSrcDS, int bStrict,
2658 : CSLConstList papszOptions,
2659 : GDALProgressFunc pfnProgress,
2660 : void *pProgressData)
2661 :
2662 : {
2663 104 : CPLStringList aosOptions(CSLDuplicate(papszOptions));
2664 104 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
2665 :
2666 52 : std::unique_ptr<TileDBRasterDataset> poDstDS;
2667 :
2668 52 : if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
2669 : {
2670 : // TileDB schemas are fixed
2671 0 : CPLError(CE_Failure, CPLE_NotSupported,
2672 : "TileDB driver does not support "
2673 : "appending to an existing schema.");
2674 0 : return nullptr;
2675 : }
2676 :
2677 : CSLConstList papszSrcSubDatasets =
2678 52 : poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
2679 :
2680 52 : if (papszSrcSubDatasets == nullptr)
2681 : {
2682 51 : const int nBands = poSrcDS->GetRasterCount();
2683 :
2684 51 : if (nBands > 0)
2685 : {
2686 51 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
2687 51 : GDALDataType eType = poBand->GetRasterDataType();
2688 :
2689 85 : for (int i = 2; i <= nBands; ++i)
2690 : {
2691 34 : if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
2692 : {
2693 0 : CPLError(CE_Failure, CPLE_NotSupported,
2694 : "TileDB driver does not support "
2695 : "source dataset with different band data types.");
2696 0 : return nullptr;
2697 : }
2698 : }
2699 :
2700 51 : poDstDS.reset(TileDBRasterDataset::Create(
2701 : osArrayPath, poSrcDS->GetRasterXSize(),
2702 : poSrcDS->GetRasterYSize(), nBands, eType, papszOptions));
2703 :
2704 51 : if (!poDstDS)
2705 : {
2706 3 : return nullptr;
2707 : }
2708 :
2709 130 : for (int i = 1; i <= nBands; ++i)
2710 : {
2711 82 : int bHasNoData = FALSE;
2712 : const double dfNoData =
2713 82 : poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
2714 82 : if (bHasNoData)
2715 3 : poDstDS->GetRasterBand(i)->SetNoDataValue(dfNoData);
2716 : }
2717 :
2718 : CPLErr eErr =
2719 48 : GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
2720 : pfnProgress, pProgressData);
2721 :
2722 48 : if (eErr != CE_None)
2723 : {
2724 0 : CPLError(eErr, CPLE_AppDefined,
2725 : "Error copying raster to TileDB.");
2726 0 : return nullptr;
2727 : }
2728 : }
2729 : else
2730 : {
2731 0 : CPLError(CE_Failure, CPLE_NotSupported,
2732 : "TileDB driver does not support "
2733 : "source datasets with zero bands.");
2734 0 : return nullptr;
2735 : }
2736 : }
2737 : else
2738 : {
2739 1 : if (bStrict)
2740 : {
2741 0 : CPLError(CE_Failure, CPLE_NotSupported,
2742 : "TileDB driver does not support copying "
2743 : "subdatasets in strict mode.");
2744 0 : return nullptr;
2745 : }
2746 :
2747 2 : if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
2748 1 : CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
2749 : {
2750 0 : CPLError(CE_Failure, CPLE_NotSupported,
2751 : "Changing block size is not supported when copying "
2752 : "subdatasets.");
2753 0 : return nullptr;
2754 : }
2755 :
2756 1 : const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
2757 : const int nMaxFiles =
2758 1 : atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
2759 :
2760 1 : aosOptions.SetNameValue("CREATE_GROUP", "NO");
2761 :
2762 1 : if (nSubDatasetCount <= nMaxFiles)
2763 : {
2764 : const char *pszSource =
2765 1 : CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
2766 1 : if (pszSource)
2767 : {
2768 : std::unique_ptr<GDALDataset> poSubDataset(
2769 1 : GDALDataset::Open(pszSource));
2770 1 : if (poSubDataset && poSubDataset->GetRasterCount() > 0)
2771 : {
2772 1 : GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
2773 :
2774 1 : TileDBRasterDataset::SetBlockSize(poBand, aosOptions);
2775 1 : poDstDS.reset(TileDBRasterDataset::CreateLL(
2776 : osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
2777 1 : poBand->GetRasterDataType(), aosOptions.List()));
2778 :
2779 1 : if (poDstDS)
2780 : {
2781 1 : if (!poDstDS->DeferredCreate(
2782 : /* bCreateArray = */ false))
2783 0 : return nullptr;
2784 :
2785 1 : if (TileDBRasterDataset::CopySubDatasets(
2786 : poSrcDS, poDstDS.get(), pfnProgress,
2787 1 : pProgressData) != CE_None)
2788 : {
2789 0 : poDstDS.reset();
2790 0 : CPLError(CE_Failure, CPLE_AppDefined,
2791 : "Unable to copy subdatasets.");
2792 : }
2793 : }
2794 : }
2795 : }
2796 : }
2797 : else
2798 : {
2799 0 : CPLError(CE_Failure, CPLE_AppDefined,
2800 : "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
2801 : }
2802 : }
2803 :
2804 : // TODO Supporting mask bands is a possible future task
2805 49 : if (poDstDS != nullptr)
2806 : {
2807 49 : int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
2808 49 : poDstDS->CloneInfo(poSrcDS, nCloneFlags);
2809 :
2810 49 : if (poDstDS->FlushCache(false) != CE_None)
2811 : {
2812 0 : CPLError(CE_Failure, CPLE_AppDefined, "FlushCache() failed");
2813 0 : return nullptr;
2814 : }
2815 :
2816 49 : return poDstDS.release();
2817 : }
2818 0 : return nullptr;
2819 : }
2820 :
2821 : /************************************************************************/
2822 : /* LoadOverviews() */
2823 : /************************************************************************/
2824 :
2825 28 : void TileDBRasterDataset::LoadOverviews()
2826 : {
2827 28 : if (m_bLoadOverviewsDone)
2828 23 : return;
2829 5 : m_bLoadOverviewsDone = true;
2830 :
2831 : // NOTE: read overview_model.rst for a high level explanation of overviews
2832 : // are stored.
2833 :
2834 5 : if (!m_bDatasetInGroup)
2835 0 : return;
2836 :
2837 5 : CPLStringList aosOpenOptions;
2838 5 : if (nTimestamp)
2839 : {
2840 : aosOpenOptions.SetNameValue("TILEDB_TIMESTAMP",
2841 0 : CPLSPrintf("%" PRIu64, nTimestamp));
2842 : }
2843 5 : if (!m_osConfigFilename.empty())
2844 : {
2845 : aosOpenOptions.SetNameValue("TILEDB_CONFIG",
2846 0 : m_osConfigFilename.c_str());
2847 : }
2848 10 : for (int i = 0; i < m_nOverviewCountFromMetadata; ++i)
2849 : {
2850 5 : const std::string osArrayName = CPLSPrintf("l_%d", 1 + i);
2851 : const std::string osOvrDatasetName =
2852 5 : CPLFormFilenameSafe(GetDescription(), osArrayName.c_str(), nullptr);
2853 :
2854 5 : GDALOpenInfo oOpenInfo(osOvrDatasetName.c_str(), eAccess);
2855 5 : oOpenInfo.papszOpenOptions = aosOpenOptions.List();
2856 : auto poOvrDS = std::unique_ptr<GDALDataset>(
2857 5 : Open(&oOpenInfo, tiledb::Object::Type::Array));
2858 5 : if (!poOvrDS)
2859 0 : return;
2860 5 : if (poOvrDS->GetRasterCount() != nBands)
2861 : {
2862 0 : CPLError(CE_Failure, CPLE_AppDefined,
2863 : "Overview %s has not the same number of bands as full "
2864 : "resolution dataset",
2865 : osOvrDatasetName.c_str());
2866 0 : return;
2867 : }
2868 5 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
2869 : }
2870 : }
2871 :
2872 : /************************************************************************/
2873 : /* IBuildOverviews() */
2874 : /************************************************************************/
2875 :
2876 13 : CPLErr TileDBRasterDataset::IBuildOverviews(
2877 : const char *pszResampling, int nOverviews, const int *panOverviewList,
2878 : int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
2879 : void *pProgressData, CSLConstList papszOptions)
2880 : {
2881 : // NOTE: read overview_model.rst for a high level explanation of overviews
2882 : // are stored.
2883 :
2884 13 : if (eAccess == GA_ReadOnly)
2885 : {
2886 3 : if (!CPLTestBool(
2887 : CPLGetConfigOption("TILEDB_GEOTIFF_OVERVIEWS", "FALSE")))
2888 : {
2889 2 : ReportError(
2890 : CE_Failure, CPLE_NotSupported,
2891 : "Cannot %s overviews in TileDB format in read-only mode",
2892 : nOverviews ? "create" : "delete");
2893 2 : return CE_Failure;
2894 : }
2895 :
2896 : // GeoTIFF overviews. This used to be supported before GDAL 3.10
2897 : // although likely not desirable.
2898 1 : return GDALPamDataset::IBuildOverviews(
2899 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2900 1 : pfnProgress, pProgressData, papszOptions);
2901 : }
2902 :
2903 10 : if (nBands == 0)
2904 : {
2905 0 : return CE_Failure;
2906 : }
2907 :
2908 : // If we already have PAM overview (i.e. GeoTIFF based), go through PAM
2909 10 : if (cpl::down_cast<GDALPamRasterBand *>(GetRasterBand(1))
2910 10 : ->GDALPamRasterBand::GetOverviewCount() > 0)
2911 : {
2912 1 : return GDALPamDataset::IBuildOverviews(
2913 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2914 1 : pfnProgress, pProgressData, papszOptions);
2915 : }
2916 :
2917 9 : if (!m_bDatasetInGroup)
2918 : {
2919 2 : ReportError(CE_Failure, CPLE_NotSupported,
2920 : "IBuildOverviews() only supported for datasets created "
2921 : "with CREATE_GROUP=YES");
2922 2 : return CE_Failure;
2923 : }
2924 :
2925 : /* -------------------------------------------------------------------- */
2926 : /* Our overview support currently only works safely if all */
2927 : /* bands are handled at the same time. */
2928 : /* -------------------------------------------------------------------- */
2929 7 : if (nListBands != nBands)
2930 : {
2931 0 : ReportError(CE_Failure, CPLE_NotSupported,
2932 : "Generation of TileDB overviews currently only "
2933 : "supported when operating on all bands. "
2934 : "Operation failed.");
2935 0 : return CE_Failure;
2936 : }
2937 :
2938 : // Force loading existing overviews
2939 7 : if (m_nOverviewCountFromMetadata)
2940 4 : GetRasterBand(1)->GetOverviewCount();
2941 7 : m_bLoadOverviewsDone = true;
2942 :
2943 : /* -------------------------------------------------------------------- */
2944 : /* Deletes existing overviews if requested. */
2945 : /* -------------------------------------------------------------------- */
2946 7 : if (nOverviews == 0)
2947 : {
2948 2 : CPLErr eErr = CE_None;
2949 :
2950 : // Unlink arrays from he group
2951 : try
2952 : {
2953 6 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
2954 4 : for (auto &&poODS : m_apoOverviewDS)
2955 : {
2956 2 : group.remove_member(poODS->GetDescription());
2957 : }
2958 2 : group.close();
2959 : }
2960 0 : catch (const tiledb::TileDBError &e)
2961 : {
2962 0 : eErr = CE_Failure;
2963 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2964 : }
2965 :
2966 2 : tiledb::VFS vfs(*m_ctx, m_ctx->config());
2967 :
2968 : // Delete arrays
2969 4 : for (auto &&poODS : m_apoOverviewDS)
2970 : {
2971 : try
2972 : {
2973 2 : CPL_IGNORE_RET_VAL(poODS->Close());
2974 2 : tiledb::Array::delete_array(*m_ctx, poODS->GetDescription());
2975 2 : if (vfs.is_dir(poODS->GetDescription()))
2976 : {
2977 2 : vfs.remove_dir(poODS->GetDescription());
2978 : }
2979 : }
2980 0 : catch (const tiledb::TileDBError &e)
2981 : {
2982 0 : eErr = CE_Failure;
2983 0 : CPLError(CE_Failure, CPLE_AppDefined,
2984 : "Array::delete_array(%s) failed: %s",
2985 0 : poODS->GetDescription(), e.what());
2986 : }
2987 2 : m_apoOverviewDSRemoved.emplace_back(std::move(poODS));
2988 : }
2989 :
2990 2 : m_apoOverviewDS.clear();
2991 2 : m_nOverviewCountFromMetadata = 0;
2992 2 : MarkPamDirty();
2993 2 : return eErr;
2994 : }
2995 :
2996 : /* -------------------------------------------------------------------- */
2997 : /* Establish which of the overview levels we already have, and */
2998 : /* which are new. */
2999 : /* -------------------------------------------------------------------- */
3000 10 : std::vector<bool> abRequireNewOverview(nOverviews, true);
3001 10 : for (int i = 0; i < nOverviews; ++i)
3002 : {
3003 5 : const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
3004 5 : const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
3005 :
3006 6 : for (const auto &poODS : m_apoOverviewDS)
3007 : {
3008 : const int nOvFactor =
3009 2 : GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
3010 : poODS->GetRasterYSize(), GetRasterYSize());
3011 :
3012 : // If we already have a 1x1 overview and this new one would result
3013 : // in it too, then don't create it.
3014 2 : if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
3015 2 : nOXSize == 1 && nOYSize == 1)
3016 : {
3017 0 : abRequireNewOverview[i] = false;
3018 0 : break;
3019 : }
3020 :
3021 3 : if (nOvFactor == panOverviewList[i] ||
3022 1 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3023 : GetRasterXSize(),
3024 : GetRasterYSize()))
3025 : {
3026 1 : abRequireNewOverview[i] = false;
3027 1 : break;
3028 : }
3029 : }
3030 :
3031 5 : if (abRequireNewOverview[i])
3032 : {
3033 4 : CPLStringList aosCreationOptions;
3034 4 : aosCreationOptions.SetNameValue("CREATE_GROUP", "NO");
3035 : aosCreationOptions.SetNameValue(
3036 4 : GDALMD_NBITS, CPLString().Printf("%d", nBitsPerSample));
3037 : aosCreationOptions.SetNameValue(GDALMD_INTERLEAVE,
3038 4 : index_type_name(eIndexMode));
3039 4 : if (nTimestamp)
3040 : {
3041 : aosCreationOptions.SetNameValue(
3042 0 : "TILEDB_TIMESTAMP", CPLSPrintf("%" PRIu64, nTimestamp));
3043 : }
3044 4 : if (!m_osConfigFilename.empty())
3045 : {
3046 : aosCreationOptions.SetNameValue("TILEDB_CONFIG",
3047 0 : m_osConfigFilename.c_str());
3048 : }
3049 :
3050 : const std::string osArrayName =
3051 4 : CPLSPrintf("l_%d", 1 + int(m_apoOverviewDS.size()));
3052 : const std::string osOvrDatasetName = CPLFormFilenameSafe(
3053 4 : GetDescription(), osArrayName.c_str(), nullptr);
3054 :
3055 : auto poOvrDS = std::unique_ptr<TileDBRasterDataset>(
3056 : Create(osOvrDatasetName.c_str(), nOXSize, nOYSize, nBands,
3057 : GetRasterBand(1)->GetRasterDataType(),
3058 4 : aosCreationOptions.List()));
3059 4 : if (!poOvrDS)
3060 0 : return CE_Failure;
3061 :
3062 : // Apply nodata from main dataset
3063 16 : for (int j = 0; j < nBands; ++j)
3064 : {
3065 12 : int bHasNoData = FALSE;
3066 : const double dfNoData =
3067 12 : GetRasterBand(j + 1)->GetNoDataValue(&bHasNoData);
3068 12 : if (bHasNoData)
3069 12 : poOvrDS->GetRasterBand(j + 1)->SetNoDataValue(dfNoData);
3070 : }
3071 :
3072 : // Apply georeferencing from main dataset
3073 4 : poOvrDS->SetSpatialRef(GetSpatialRef());
3074 4 : GDALGeoTransform gt;
3075 4 : if (GetGeoTransform(gt) == CE_None)
3076 : {
3077 4 : gt.Rescale(static_cast<double>(nRasterXSize) / nOXSize,
3078 4 : static_cast<double>(nRasterYSize) / nOYSize);
3079 4 : poOvrDS->SetGeoTransform(gt);
3080 : }
3081 :
3082 4 : poOvrDS->DeferredCreate(/* bCreateArray = */ true);
3083 :
3084 : try
3085 : {
3086 12 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
3087 4 : group.add_member(osOvrDatasetName, false);
3088 4 : group.close();
3089 : }
3090 0 : catch (const tiledb::TileDBError &e)
3091 : {
3092 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
3093 : }
3094 :
3095 4 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
3096 : }
3097 : }
3098 :
3099 5 : m_nOverviewCountFromMetadata = static_cast<int>(m_apoOverviewDS.size());
3100 5 : MarkPamDirty();
3101 :
3102 : /* -------------------------------------------------------------------- */
3103 : /* Refresh/generate overviews that are listed. */
3104 : /* -------------------------------------------------------------------- */
3105 10 : std::vector<GDALRasterBand *> apoSrcBands;
3106 10 : std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands;
3107 5 : CPLErr eErr = CE_None;
3108 : const auto osNormalizedResampling =
3109 5 : GDALGetNormalizedOvrResampling(pszResampling);
3110 20 : for (int iBand = 0; eErr == CE_None && iBand < nBands; iBand++)
3111 : {
3112 15 : apoSrcBands.push_back(GetRasterBand(iBand + 1));
3113 30 : std::vector<GDALRasterBand *> apoOverviewBands;
3114 :
3115 : std::vector<bool> abAlreadyUsedOverviewBand(m_apoOverviewDS.size(),
3116 30 : false);
3117 :
3118 30 : for (int i = 0; i < nOverviews; i++)
3119 : {
3120 15 : bool bFound = false;
3121 18 : for (size_t j = 0; j < m_apoOverviewDS.size(); ++j)
3122 : {
3123 18 : if (!abAlreadyUsedOverviewBand[j])
3124 : {
3125 18 : auto &poODS = m_apoOverviewDS[j];
3126 18 : int nOvFactor = GDALComputeOvFactor(
3127 : poODS->GetRasterXSize(), nRasterXSize,
3128 : poODS->GetRasterYSize(), nRasterYSize);
3129 :
3130 21 : if (nOvFactor == panOverviewList[i] ||
3131 3 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3132 : nRasterXSize,
3133 : nRasterYSize))
3134 : {
3135 15 : abAlreadyUsedOverviewBand[j] = true;
3136 15 : auto poOvrBand = poODS->GetRasterBand(iBand + 1);
3137 15 : if (!osNormalizedResampling.empty())
3138 : {
3139 : // Store resampling method in band metadata, as it
3140 : // can be used by the gdaladdo utilities to refresh
3141 : // existing overviews with the method previously
3142 : // used
3143 15 : poOvrBand->SetMetadataItem(
3144 15 : "RESAMPLING", osNormalizedResampling.c_str());
3145 : }
3146 15 : apoOverviewBands.push_back(poOvrBand);
3147 15 : bFound = true;
3148 15 : break;
3149 : }
3150 : }
3151 : }
3152 15 : if (!bFound)
3153 : {
3154 0 : CPLError(CE_Failure, CPLE_AppDefined,
3155 : "Could not find dataset corresponding to ov factor %d",
3156 0 : panOverviewList[i]);
3157 0 : eErr = CE_Failure;
3158 : }
3159 : }
3160 15 : if (iBand > 0)
3161 : {
3162 10 : CPLAssert(apoOverviewBands.size() == aapoOverviewBands[0].size());
3163 : }
3164 15 : aapoOverviewBands.emplace_back(std::move(apoOverviewBands));
3165 : }
3166 :
3167 5 : if (eErr == CE_None)
3168 : {
3169 5 : eErr = GDALRegenerateOverviewsMultiBand(apoSrcBands, aapoOverviewBands,
3170 : pszResampling, pfnProgress,
3171 : pProgressData, papszOptions);
3172 : }
3173 :
3174 5 : return eErr;
3175 : }
|