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 : virtual CPLErr IReadBlock(int, int, void *) override;
47 : virtual CPLErr IWriteBlock(int, int, void *) override;
48 : virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
49 : GDALDataType, GSpacing, GSpacing,
50 : GDALRasterIOExtraArg *psExtraArg) override;
51 : virtual 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_Byte:
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_Byte;
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'.\n");
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.\n");
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 70 : GDALColorInterp TileDBRasterBand::GetColorInterpretation()
442 :
443 : {
444 70 : if (poGDS->nBands == 1)
445 20 : return GCI_GrayIndex;
446 :
447 50 : if (nBand == 1)
448 20 : return GCI_RedBand;
449 :
450 30 : else if (nBand == 2)
451 15 : return GCI_GreenBand;
452 :
453 15 : else if (nBand == 3)
454 15 : return GCI_BlueBand;
455 :
456 0 : return GCI_AlphaBand;
457 : }
458 :
459 : /************************************************************************/
460 : /* GetNoDataValue() */
461 : /************************************************************************/
462 :
463 79 : double TileDBRasterBand::GetNoDataValue(int *pbHasNoData)
464 : {
465 79 : if (pbHasNoData)
466 76 : *pbHasNoData = false;
467 79 : if (m_bNoDataSet)
468 : {
469 11 : if (pbHasNoData)
470 11 : *pbHasNoData = true;
471 11 : return m_dfNoData;
472 : }
473 68 : if (!poGDS->m_bDeferredCreateHasBeenSuccessful)
474 0 : return 0.0;
475 68 : if (!poGDS->m_array)
476 0 : return 0.0;
477 68 : double dfNoData = 0.0;
478 : try
479 : {
480 68 : const void *value = nullptr;
481 68 : 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 68 : auto attr = (poGDS->m_roArray ? poGDS->m_roArray : poGDS->m_array)
486 68 : ->schema()
487 136 : .attribute(osAttrName);
488 68 : attr.get_fill_value(&value, &size);
489 68 : if (value &&
490 68 : size == static_cast<uint64_t>(GDALGetDataTypeSizeBytes(eDataType)))
491 : {
492 68 : switch (eDataType)
493 : {
494 51 : case GDT_Byte:
495 51 : dfNoData = *static_cast<const uint8_t *>(value);
496 51 : 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 68 : if (pbHasNoData)
540 65 : *pbHasNoData = true;
541 : }
542 : }
543 0 : catch (const tiledb::TileDBError &e)
544 : {
545 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
546 : }
547 68 : 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_Byte:
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()
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 : char **TileDBRasterDataset::GetMetadata(const char *pszDomain)
1284 :
1285 : {
1286 216 : if (pszDomain != nullptr && EQUAL(pszDomain, "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 258 : poDS->bStats =
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 : char **papszStructMeta = poDS->GetMetadata("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, "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 129 : const char *pszIndexMode = CSLFetchNameValue(papszStructMeta, "INTERLEAVE");
1531 :
1532 129 : if (pszIndexMode)
1533 107 : option_to_index_type(pszIndexMode, poDS->eIndexMode);
1534 :
1535 258 : std::vector<tiledb::Dimension> dims = schema.domain().dimensions();
1536 :
1537 129 : int iYDim = 0;
1538 129 : int iXDim = 1;
1539 129 : if ((dims.size() == 2) || (dims.size() == 3))
1540 : {
1541 129 : if (dims.size() == 3)
1542 : {
1543 141 : if ((pszAttr != nullptr) &&
1544 141 : (schema.attributes().count(pszAttr) == 0))
1545 : {
1546 0 : CPLError(CE_Failure, CPLE_NotSupported,
1547 : "%s attribute is not found in TileDB schema.",
1548 : pszAttr);
1549 0 : return nullptr;
1550 : }
1551 :
1552 123 : if (poDS->eIndexMode == PIXEL)
1553 15 : std::rotate(dims.begin(), dims.begin() + 2, dims.end());
1554 :
1555 123 : if (dims[0].type() != TILEDB_UINT64)
1556 : {
1557 0 : const char *pszTypeName = "";
1558 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1559 0 : CPLError(CE_Failure, CPLE_NotSupported,
1560 : "Unsupported BAND dimension type: %s", pszTypeName);
1561 0 : return nullptr;
1562 : }
1563 123 : poDS->nBandStart = dims[0].domain<uint64_t>().first;
1564 123 : const uint64_t nBandEnd = dims[0].domain<uint64_t>().second;
1565 246 : if (nBandEnd < poDS->nBandStart ||
1566 123 : nBandEnd - poDS->nBandStart >
1567 123 : static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1568 : {
1569 0 : CPLError(CE_Failure, CPLE_NotSupported,
1570 : "Invalid bounds for BAND dimension.");
1571 0 : return nullptr;
1572 : }
1573 123 : poDS->nBands = static_cast<int>(nBandEnd - poDS->nBandStart + 1);
1574 123 : iYDim = 1;
1575 123 : iXDim = 2;
1576 : }
1577 : else
1578 : {
1579 : const char *pszBands =
1580 6 : poDS->GetMetadataItem("NUM_BANDS", "IMAGE_STRUCTURE");
1581 6 : if (pszBands)
1582 : {
1583 1 : poDS->nBands = atoi(pszBands);
1584 : }
1585 :
1586 6 : poDS->eIndexMode = ATTRIBUTES;
1587 : }
1588 : }
1589 : else
1590 : {
1591 0 : CPLError(CE_Failure, CPLE_AppDefined,
1592 : "Wrong number of dimensions %d: expected 2 or 3.",
1593 0 : static_cast<int>(dims.size()));
1594 0 : return nullptr;
1595 : }
1596 :
1597 258 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
1598 129 : !GDALCheckBandCount(poDS->nBands, /*bIsZeroAllowed=*/true))
1599 : {
1600 0 : return nullptr;
1601 : }
1602 :
1603 129 : if (dims[iYDim].type() != TILEDB_UINT64)
1604 : {
1605 0 : const char *pszTypeName = "";
1606 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1607 0 : CPLError(CE_Failure, CPLE_NotSupported,
1608 : "Unsupported Y dimension type: %s", pszTypeName);
1609 0 : return nullptr;
1610 : }
1611 129 : if (!pszYSize)
1612 : {
1613 16 : const uint64_t nStart = dims[iYDim].domain<uint64_t>().first;
1614 16 : const uint64_t nEnd = dims[iYDim].domain<uint64_t>().second;
1615 32 : if (nStart != 0 ||
1616 16 : nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1617 : {
1618 0 : CPLError(CE_Failure, CPLE_NotSupported,
1619 : "Invalid bounds for Y dimension.");
1620 0 : return nullptr;
1621 : }
1622 16 : poDS->nRasterYSize = static_cast<int>(nEnd - nStart + 1);
1623 : }
1624 129 : const uint64_t nBlockYSize = dims[iYDim].tile_extent<uint64_t>();
1625 129 : if (nBlockYSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
1626 : {
1627 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large block Y size.");
1628 0 : return nullptr;
1629 : }
1630 129 : poDS->nBlockYSize = static_cast<int>(nBlockYSize);
1631 :
1632 129 : if (dims[iXDim].type() != TILEDB_UINT64)
1633 : {
1634 0 : const char *pszTypeName = "";
1635 0 : tiledb_datatype_to_str(dims[0].type(), &pszTypeName);
1636 0 : CPLError(CE_Failure, CPLE_NotSupported,
1637 : "Unsupported Y dimension type: %s", pszTypeName);
1638 0 : return nullptr;
1639 : }
1640 129 : if (!pszXSize)
1641 : {
1642 16 : const uint64_t nStart = dims[iXDim].domain<uint64_t>().first;
1643 16 : const uint64_t nEnd = dims[iXDim].domain<uint64_t>().second;
1644 32 : if (nStart != 0 ||
1645 16 : nEnd > static_cast<uint64_t>(std::numeric_limits<int>::max() - 1))
1646 : {
1647 0 : CPLError(CE_Failure, CPLE_NotSupported,
1648 : "Invalid bounds for X dimension.");
1649 0 : return nullptr;
1650 : }
1651 16 : poDS->nRasterXSize = static_cast<int>(nEnd - nStart + 1);
1652 : }
1653 129 : const uint64_t nBlockXSize = dims[iXDim].tile_extent<uint64_t>();
1654 129 : if (nBlockXSize > static_cast<uint64_t>(std::numeric_limits<int>::max()))
1655 : {
1656 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large block X size.");
1657 0 : return nullptr;
1658 : }
1659 129 : poDS->nBlockXSize = static_cast<int>(nBlockXSize);
1660 :
1661 129 : poDS->nBlocksX = DIV_ROUND_UP(poDS->nRasterXSize, poDS->nBlockXSize);
1662 129 : poDS->nBlocksY = DIV_ROUND_UP(poDS->nRasterYSize, poDS->nBlockYSize);
1663 :
1664 129 : if (dims.size() == 3)
1665 : {
1666 : // Create band information objects.
1667 844 : for (int i = 1; i <= poDS->nBands; ++i)
1668 : {
1669 721 : if (pszAttr == nullptr)
1670 189 : poDS->SetBand(i, new TileDBRasterBand(poDS.get(), i));
1671 : else
1672 1064 : poDS->SetBand(
1673 1064 : i, new TileDBRasterBand(poDS.get(), i, CPLString(pszAttr)));
1674 : }
1675 : }
1676 : else // subdatasets or only attributes
1677 : {
1678 7 : if ((poOpenInfo->eAccess == GA_Update) &&
1679 1 : (poDS->GetMetadata("SUBDATASETS") != nullptr))
1680 : {
1681 0 : CPLError(CE_Failure, CPLE_NotSupported,
1682 : "The TileDB driver does not support update access "
1683 : "to subdatasets.");
1684 0 : return nullptr;
1685 : }
1686 :
1687 6 : if (!osSubdataset.empty())
1688 : {
1689 : // do we have the attribute in the schema
1690 2 : if (schema.attributes().count(osSubdataset))
1691 : {
1692 0 : poDS->SetBand(
1693 0 : 1, new TileDBRasterBand(poDS.get(), 1, osSubdataset));
1694 : }
1695 : else
1696 : {
1697 2 : if (schema.attributes().count(osSubdataset + "_1"))
1698 : {
1699 : // Create band information objects.
1700 2 : for (int i = 1; i <= poDS->nBands; ++i)
1701 : {
1702 1 : CPLString osAttr = CPLString().Printf(
1703 2 : "%s_%d", osSubdataset.c_str(), i);
1704 2 : poDS->SetBand(
1705 1 : i, new TileDBRasterBand(poDS.get(), i, osAttr));
1706 : }
1707 : }
1708 : else
1709 : {
1710 1 : CPLError(CE_Failure, CPLE_NotSupported,
1711 : "%s attribute is not found in TileDB schema.",
1712 : osSubdataset.c_str());
1713 1 : return nullptr;
1714 : }
1715 : }
1716 : }
1717 : else
1718 : {
1719 4 : char **papszMeta = poDS->GetMetadata("SUBDATASETS");
1720 4 : if (papszMeta != nullptr)
1721 : {
1722 1 : if ((CSLCount(papszMeta) / 2) == 1)
1723 : {
1724 : const char *pszSubDSName =
1725 0 : poDS->m_aosSubdatasetMD.FetchNameValueDef(
1726 : "SUBDATASET_1_NAME", "");
1727 0 : return GDALDataset::FromHandle(
1728 0 : GDALOpen(pszSubDSName, poOpenInfo->eAccess));
1729 : }
1730 : }
1731 3 : else if (poDS->eIndexMode == ATTRIBUTES)
1732 : {
1733 3 : poDS->nBands = schema.attribute_num();
1734 :
1735 : // Create band information objects.
1736 11 : for (int i = 1; i <= poDS->nBands; ++i)
1737 : {
1738 : CPLString osAttr =
1739 24 : TILEDB_VALUES + CPLString().Printf("_%i", i);
1740 16 : poDS->SetBand(i,
1741 8 : new TileDBRasterBand(poDS.get(), i, osAttr));
1742 : }
1743 : }
1744 : else
1745 : {
1746 0 : CPLError(CE_Failure, CPLE_NotSupported,
1747 : "%s is missing required TileDB subdataset metadata.",
1748 : osURI.c_str());
1749 0 : return nullptr;
1750 : }
1751 : }
1752 : }
1753 :
1754 : // reload metadata now that bands are created to populate band metadata
1755 128 : poDS->TryLoadCachedXML(nullptr, false);
1756 :
1757 256 : tiledb::VFS vfs(*poDS->m_ctx, poDS->m_ctx->config());
1758 :
1759 128 : if (!STARTS_WITH_CI(osURI, "TILEDB:") && vfs.is_dir(osURI))
1760 128 : poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
1761 : else
1762 0 : CPLError(CE_Warning, CPLE_AppDefined,
1763 : "Overviews not supported for network writes.");
1764 :
1765 128 : return poDS.release();
1766 : }
1767 :
1768 : /************************************************************************/
1769 : /* CreateAttribute() */
1770 : /************************************************************************/
1771 :
1772 : template <class T, class NoDataT = T>
1773 129 : static tiledb::Attribute CreateAttribute(tiledb::Context &ctx,
1774 : const std::string &osAttrName,
1775 : tiledb::FilterList &filterList,
1776 : bool bHasFillValue, double dfFillValue)
1777 : {
1778 129 : auto attr = tiledb::Attribute::create<T>(ctx, osAttrName, filterList);
1779 129 : if (bHasFillValue && GDALIsValueInRange<NoDataT>(dfFillValue))
1780 : {
1781 30 : const auto nVal = static_cast<NoDataT>(dfFillValue);
1782 30 : if (dfFillValue == static_cast<double>(nVal))
1783 : {
1784 : if constexpr (sizeof(T) == sizeof(NoDataT))
1785 : {
1786 26 : attr.set_fill_value(&nVal, sizeof(nVal));
1787 : }
1788 : else
1789 : {
1790 4 : T aVal = {nVal, nVal};
1791 4 : attr.set_fill_value(&aVal, sizeof(aVal));
1792 : }
1793 : }
1794 : }
1795 129 : return attr;
1796 : }
1797 :
1798 : /************************************************************************/
1799 : /* CreateAttribute() */
1800 : /************************************************************************/
1801 :
1802 123 : CPLErr TileDBRasterDataset::CreateAttribute(GDALDataType eType,
1803 : const CPLString &osAttrName,
1804 : const int nSubRasterCount,
1805 : bool bHasFillValue,
1806 : double dfFillValue)
1807 : {
1808 : try
1809 : {
1810 252 : for (int i = 0; i < nSubRasterCount; ++i)
1811 : {
1812 129 : CPLString osName(osAttrName);
1813 : // a few special cases
1814 : // remove any leading slashes or
1815 : // additional slashes as in the case of hdf5
1816 129 : if STARTS_WITH (osName, "//")
1817 : {
1818 2 : osName = osName.substr(2);
1819 : }
1820 :
1821 129 : osName.replaceAll("/", "_");
1822 129 : CPLString osPrettyName = osName;
1823 :
1824 129 : if ((eIndexMode == ATTRIBUTES) ||
1825 115 : ((m_bHasSubDatasets) && (nSubRasterCount > 1)))
1826 : {
1827 14 : osName = CPLString().Printf("%s_%d", osName.c_str(), i + 1);
1828 : }
1829 :
1830 129 : switch (eType)
1831 : {
1832 53 : case GDT_Byte:
1833 : {
1834 159 : m_schema->add_attribute(::CreateAttribute<unsigned char>(
1835 53 : *m_ctx, osName, *m_filterList, bHasFillValue,
1836 106 : dfFillValue));
1837 53 : nBitsPerSample = 8;
1838 53 : break;
1839 : }
1840 4 : case GDT_Int8:
1841 : {
1842 4 : m_schema->add_attribute(
1843 8 : ::CreateAttribute<int8_t>(*m_ctx, osName, *m_filterList,
1844 8 : bHasFillValue, dfFillValue));
1845 4 : nBitsPerSample = 8;
1846 4 : break;
1847 : }
1848 5 : case GDT_UInt16:
1849 : {
1850 15 : m_schema->add_attribute(::CreateAttribute<uint16_t>(
1851 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1852 10 : dfFillValue));
1853 5 : nBitsPerSample = 16;
1854 5 : break;
1855 : }
1856 5 : case GDT_UInt32:
1857 : {
1858 15 : m_schema->add_attribute(::CreateAttribute<uint32_t>(
1859 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1860 10 : dfFillValue));
1861 5 : nBitsPerSample = 32;
1862 5 : break;
1863 : }
1864 4 : case GDT_UInt64:
1865 : {
1866 12 : m_schema->add_attribute(::CreateAttribute<uint64_t>(
1867 4 : *m_ctx, osName, *m_filterList, bHasFillValue,
1868 8 : dfFillValue));
1869 4 : nBitsPerSample = 64;
1870 4 : break;
1871 : }
1872 5 : case GDT_Int16:
1873 : {
1874 15 : m_schema->add_attribute(::CreateAttribute<int16_t>(
1875 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1876 10 : dfFillValue));
1877 5 : nBitsPerSample = 16;
1878 5 : break;
1879 : }
1880 7 : case GDT_Int32:
1881 : {
1882 21 : m_schema->add_attribute(::CreateAttribute<int32_t>(
1883 7 : *m_ctx, osName, *m_filterList, bHasFillValue,
1884 14 : dfFillValue));
1885 7 : nBitsPerSample = 32;
1886 7 : break;
1887 : }
1888 4 : case GDT_Int64:
1889 : {
1890 12 : m_schema->add_attribute(::CreateAttribute<int64_t>(
1891 4 : *m_ctx, osName, *m_filterList, bHasFillValue,
1892 8 : dfFillValue));
1893 4 : nBitsPerSample = 64;
1894 4 : break;
1895 : }
1896 12 : case GDT_Float32:
1897 : {
1898 12 : m_schema->add_attribute(
1899 24 : ::CreateAttribute<float>(*m_ctx, osName, *m_filterList,
1900 24 : bHasFillValue, dfFillValue));
1901 12 : nBitsPerSample = 32;
1902 12 : break;
1903 : }
1904 8 : case GDT_Float64:
1905 : {
1906 8 : m_schema->add_attribute(
1907 16 : ::CreateAttribute<double>(*m_ctx, osName, *m_filterList,
1908 16 : bHasFillValue, dfFillValue));
1909 8 : nBitsPerSample = 64;
1910 8 : break;
1911 : }
1912 5 : case GDT_CInt16:
1913 : {
1914 5 : m_schema->add_attribute(
1915 10 : ::CreateAttribute<int16_t[2], int16_t>(
1916 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1917 10 : dfFillValue));
1918 5 : nBitsPerSample = 16;
1919 5 : break;
1920 : }
1921 5 : case GDT_CInt32:
1922 : {
1923 5 : m_schema->add_attribute(
1924 10 : ::CreateAttribute<int32_t[2], int32_t>(
1925 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1926 10 : dfFillValue));
1927 5 : nBitsPerSample = 32;
1928 5 : break;
1929 : }
1930 5 : case GDT_CFloat32:
1931 : {
1932 15 : m_schema->add_attribute(::CreateAttribute<float[2], float>(
1933 5 : *m_ctx, osName, *m_filterList, bHasFillValue,
1934 10 : dfFillValue));
1935 5 : nBitsPerSample = 32;
1936 5 : break;
1937 : }
1938 7 : case GDT_CFloat64:
1939 : {
1940 7 : m_schema->add_attribute(
1941 14 : ::CreateAttribute<double[2], double>(
1942 7 : *m_ctx, osName, *m_filterList, bHasFillValue,
1943 14 : dfFillValue));
1944 7 : nBitsPerSample = 64;
1945 7 : break;
1946 : }
1947 0 : default:
1948 0 : return CE_Failure;
1949 : }
1950 :
1951 129 : if ((m_bHasSubDatasets) && (i == 0))
1952 : {
1953 8 : CPLString osDim;
1954 8 : switch (nSubRasterCount)
1955 : {
1956 0 : case 2:
1957 0 : osDim.Printf("%dx%d", nRasterXSize, nRasterYSize);
1958 0 : break;
1959 8 : default:
1960 : osDim.Printf("%dx%dx%d", nSubRasterCount, nRasterXSize,
1961 8 : nRasterYSize);
1962 8 : break;
1963 : }
1964 :
1965 8 : const int nSubDataCount = 1 + m_aosSubdatasetMD.size() / 2;
1966 : m_aosSubdatasetMD.SetNameValue(
1967 16 : CPLString().Printf("SUBDATASET_%d_NAME", nSubDataCount),
1968 24 : CPLString().Printf("%s", osPrettyName.c_str()));
1969 :
1970 : m_aosSubdatasetMD.SetNameValue(
1971 16 : CPLString().Printf("SUBDATASET_%d_DESC", nSubDataCount),
1972 8 : CPLString().Printf("[%s] %s (%s)", osDim.c_str(),
1973 : osPrettyName.c_str(),
1974 16 : GDALGetDataTypeName(eType)));
1975 :
1976 : // add to PAM metadata
1977 8 : if (!m_poSubDatasetsTree)
1978 : {
1979 3 : m_poSubDatasetsTree.reset(
1980 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
1981 : }
1982 :
1983 8 : CPLXMLNode *psSubNode = CPLCreateXMLNode(
1984 : m_poSubDatasetsTree.get(), CXT_Element, "Subdataset");
1985 8 : CPLAddXMLAttributeAndValue(psSubNode, "name",
1986 : osPrettyName.c_str());
1987 :
1988 8 : CPLXMLNode *psMetaNode = CPLCreateXMLNode(
1989 : CPLCreateXMLNode(psSubNode, CXT_Element, "PAMDataset"),
1990 : CXT_Element, "Metadata");
1991 8 : CPLAddXMLAttributeAndValue(psMetaNode, "domain",
1992 : "IMAGE_STRUCTURE");
1993 :
1994 8 : CPLAddXMLAttributeAndValue(
1995 : CPLCreateXMLElementAndValue(
1996 : psMetaNode, "MDI",
1997 16 : CPLString().Printf("%d", nRasterXSize)),
1998 : "KEY", "X_SIZE");
1999 :
2000 8 : CPLAddXMLAttributeAndValue(
2001 : CPLCreateXMLElementAndValue(
2002 : psMetaNode, "MDI",
2003 16 : CPLString().Printf("%d", nRasterYSize)),
2004 : "KEY", "Y_SIZE");
2005 :
2006 8 : CPLAddXMLAttributeAndValue(
2007 : CPLCreateXMLElementAndValue(
2008 : psMetaNode, "MDI",
2009 16 : CPLString().Printf("%s", GDALGetDataTypeName(eType))),
2010 : "KEY", "DATA_TYPE");
2011 :
2012 8 : if (m_lpoAttributeDS.size() > 0)
2013 : {
2014 6 : CPLAddXMLAttributeAndValue(
2015 : CPLCreateXMLElementAndValue(
2016 : psMetaNode, "MDI",
2017 12 : CPLString().Printf("%d", nBands)),
2018 : "KEY", "NUM_BANDS");
2019 : }
2020 : else
2021 : {
2022 2 : CPLAddXMLAttributeAndValue(
2023 : CPLCreateXMLElementAndValue(
2024 : psMetaNode, "MDI",
2025 4 : CPLString().Printf("%d", nSubRasterCount)),
2026 : "KEY", "NUM_BANDS");
2027 : }
2028 :
2029 8 : CPLAddXMLAttributeAndValue(
2030 : CPLCreateXMLElementAndValue(
2031 : psMetaNode, "MDI",
2032 16 : CPLString().Printf("%d", nBitsPerSample)),
2033 : "KEY", "NBITS");
2034 : }
2035 : }
2036 123 : return CE_None;
2037 : }
2038 0 : catch (const tiledb::TileDBError &e)
2039 : {
2040 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2041 0 : return CE_Failure;
2042 : }
2043 : }
2044 :
2045 : /************************************************************************/
2046 : /* SetBlockSize() */
2047 : /************************************************************************/
2048 :
2049 1 : void TileDBRasterDataset::SetBlockSize(GDALRasterBand *poBand,
2050 : CPLStringList &aosOptions)
2051 :
2052 : {
2053 1 : int nX = 0;
2054 1 : int nY = 0;
2055 1 : poBand->GetBlockSize(&nX, &nY);
2056 :
2057 1 : if (!aosOptions.FetchNameValue("BLOCKXSIZE"))
2058 : {
2059 1 : aosOptions.SetNameValue("BLOCKXSIZE", CPLString().Printf("%d", nX));
2060 : }
2061 :
2062 1 : if (!aosOptions.FetchNameValue("BLOCKYSIZE"))
2063 : {
2064 1 : aosOptions.SetNameValue("BLOCKYSIZE", CPLString().Printf("%d", nY));
2065 : }
2066 1 : }
2067 :
2068 : /************************************************************************/
2069 : /* CreateLL() */
2070 : /* */
2071 : /* Shared functionality between TileDBDataset::Create() and */
2072 : /* TileDBDataset::CreateCopy() for creating TileDB array based on */
2073 : /* a set of options and a configuration. */
2074 : /************************************************************************/
2075 :
2076 120 : TileDBRasterDataset *TileDBRasterDataset::CreateLL(const char *pszFilename,
2077 : int nXSize, int nYSize,
2078 : int nBandsIn,
2079 : GDALDataType eType,
2080 : CSLConstList papszOptions)
2081 : {
2082 : try
2083 : {
2084 120 : if ((nXSize <= 0 && nYSize <= 0))
2085 : {
2086 0 : return nullptr;
2087 : }
2088 :
2089 240 : auto poDS = std::make_unique<TileDBRasterDataset>();
2090 120 : poDS->nRasterXSize = nXSize;
2091 120 : poDS->nRasterYSize = nYSize;
2092 120 : poDS->eDataType = eType;
2093 120 : poDS->nBands = nBandsIn;
2094 120 : poDS->eAccess = GA_Update;
2095 :
2096 120 : if (poDS->nBands == 0) // subdatasets
2097 : {
2098 1 : poDS->eIndexMode = ATTRIBUTES;
2099 : }
2100 : else
2101 : {
2102 : const char *pszIndexMode =
2103 119 : CSLFetchNameValue(papszOptions, "INTERLEAVE");
2104 :
2105 119 : if (option_to_index_type(pszIndexMode, poDS->eIndexMode))
2106 0 : return nullptr;
2107 : }
2108 :
2109 : const char *pszConfig =
2110 120 : CSLFetchNameValue(papszOptions, "TILEDB_CONFIG");
2111 120 : if (pszConfig != nullptr)
2112 : {
2113 0 : poDS->m_osConfigFilename = pszConfig;
2114 0 : tiledb::Config cfg(pszConfig);
2115 0 : poDS->m_ctx.reset(new tiledb::Context(cfg));
2116 : }
2117 : else
2118 : {
2119 120 : poDS->m_ctx.reset(new tiledb::Context());
2120 : }
2121 :
2122 120 : if (CPLTestBool(
2123 : CSLFetchNameValueDef(papszOptions, "CREATE_GROUP", "YES")))
2124 : {
2125 110 : poDS->m_bDatasetInGroup = true;
2126 116 : tiledb::create_group(*(poDS->m_ctx.get()), pszFilename);
2127 :
2128 : {
2129 107 : tiledb::Group group(*(poDS->m_ctx.get()), pszFilename,
2130 428 : TILEDB_WRITE);
2131 107 : group.put_metadata(
2132 : DATASET_TYPE_ATTRIBUTE_NAME, TILEDB_STRING_UTF8,
2133 : static_cast<int>(strlen(RASTER_DATASET_TYPE)),
2134 : RASTER_DATASET_TYPE);
2135 :
2136 107 : group.close();
2137 : }
2138 :
2139 : // Full resolution raster array
2140 107 : poDS->m_osArrayURI =
2141 214 : CPLFormFilenameSafe(pszFilename, "l_0", nullptr);
2142 : }
2143 : else
2144 : {
2145 10 : poDS->m_osArrayURI = pszFilename;
2146 : }
2147 :
2148 : const char *pszCompression =
2149 117 : CSLFetchNameValue(papszOptions, "COMPRESSION");
2150 : const char *pszCompressionLevel =
2151 117 : CSLFetchNameValue(papszOptions, "COMPRESSION_LEVEL");
2152 :
2153 : const char *pszBlockXSize =
2154 117 : CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
2155 117 : poDS->nBlockXSize = (pszBlockXSize) ? atoi(pszBlockXSize) : 256;
2156 : const char *pszBlockYSize =
2157 117 : CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
2158 117 : poDS->nBlockYSize = (pszBlockYSize) ? atoi(pszBlockYSize) : 256;
2159 117 : poDS->bStats = CSLFetchBoolean(papszOptions, "STATS", FALSE);
2160 :
2161 : const char *pszTimestamp =
2162 117 : CSLFetchNameValue(papszOptions, "TILEDB_TIMESTAMP");
2163 117 : if (pszTimestamp != nullptr)
2164 2 : poDS->nTimestamp = std::strtoull(pszTimestamp, nullptr, 10);
2165 :
2166 : // set dimensions and attribute type for schema
2167 234 : poDS->m_schema.reset(
2168 117 : new tiledb::ArraySchema(*poDS->m_ctx, TILEDB_DENSE));
2169 117 : poDS->m_schema->set_tile_order(TILEDB_ROW_MAJOR);
2170 117 : poDS->m_schema->set_cell_order(TILEDB_ROW_MAJOR);
2171 :
2172 117 : poDS->m_filterList.reset(new tiledb::FilterList(*poDS->m_ctx));
2173 :
2174 117 : if (pszCompression != nullptr)
2175 : {
2176 0 : int nLevel = (pszCompressionLevel) ? atoi(pszCompressionLevel) : -1;
2177 0 : if (TileDBDataset::AddFilter(*(poDS->m_ctx.get()),
2178 0 : *(poDS->m_filterList.get()),
2179 0 : pszCompression, nLevel) == CE_None)
2180 : {
2181 0 : poDS->SetMetadataItem("COMPRESSION", pszCompression,
2182 0 : "IMAGE_STRUCTURE");
2183 0 : poDS->m_schema->set_coords_filter_list(*poDS->m_filterList);
2184 : }
2185 : }
2186 :
2187 234 : CPLString osAux = CPLGetBasenameSafe(pszFilename);
2188 117 : osAux += ".tdb";
2189 :
2190 234 : poDS->SetPhysicalFilename(
2191 234 : CPLFormFilenameSafe(pszFilename, osAux.c_str(), nullptr).c_str());
2192 :
2193 : // Initialize PAM information.
2194 117 : poDS->SetDescription(pszFilename);
2195 :
2196 : // Note the dimension bounds are inclusive and are expanded to the match
2197 : // the block size
2198 117 : poDS->nBlocksX = DIV_ROUND_UP(nXSize, poDS->nBlockXSize);
2199 117 : poDS->nBlocksY = DIV_ROUND_UP(nYSize, poDS->nBlockYSize);
2200 :
2201 : // register additional attributes to the pixel value, these will be
2202 : // be reported as subdatasets on future reads
2203 : CSLConstList papszAttributes =
2204 117 : CSLFetchNameValueMultiple(papszOptions, "TILEDB_ATTRIBUTE");
2205 121 : for (const char *pszAttribute : cpl::Iterate(papszAttributes))
2206 : {
2207 : // modeling additional attributes as subdatasets
2208 4 : poDS->m_bHasSubDatasets = true;
2209 : // check each attribute is a GDAL source
2210 : std::unique_ptr<GDALDataset> poAttrDS(
2211 8 : GDALDataset::Open(pszAttribute, GA_ReadOnly));
2212 :
2213 4 : if (poAttrDS != nullptr)
2214 : {
2215 : // check each is co-registered
2216 : // candidate band
2217 4 : int nAttrBands = poAttrDS->GetRasterCount();
2218 4 : if (nAttrBands > 0)
2219 : {
2220 4 : GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
2221 :
2222 4 : if ((poAttrBand->GetXSize() == poDS->nRasterXSize) &&
2223 8 : (poAttrBand->GetYSize() == poDS->nRasterYSize) &&
2224 4 : (poDS->nBands == nAttrBands))
2225 : {
2226 : // could check geotransform, but it is sufficient
2227 : // that cartesian dimensions are equal
2228 4 : poDS->m_lpoAttributeDS.push_back(std::move(poAttrDS));
2229 : }
2230 : else
2231 : {
2232 0 : CPLError(
2233 : CE_Warning, CPLE_AppDefined,
2234 : "Skipping %s as it has a different dimension\n",
2235 : pszAttribute);
2236 : }
2237 : }
2238 : else
2239 : {
2240 0 : CPLError(CE_Warning, CPLE_AppDefined,
2241 : "Skipping %s as it doesn't have any bands\n",
2242 : pszAttribute);
2243 : }
2244 : }
2245 : else
2246 : {
2247 0 : CPLError(CE_Warning, CPLE_AppDefined,
2248 : "Skipping %s, not recognized as a GDAL dataset\n",
2249 : pszAttribute);
2250 : }
2251 : }
2252 :
2253 117 : return poDS.release();
2254 : }
2255 3 : catch (const tiledb::TileDBError &e)
2256 : {
2257 3 : CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
2258 3 : return nullptr;
2259 : }
2260 : }
2261 :
2262 : /************************************************************************/
2263 : /* DeferredCreate() */
2264 : /* */
2265 : /* Create dimension, domains and attributes. and optionally the array */
2266 : /************************************************************************/
2267 :
2268 120 : bool TileDBRasterDataset::DeferredCreate(bool bCreateArray)
2269 : {
2270 120 : CPLAssert(!m_bDeferredCreateHasRun);
2271 120 : m_bDeferredCreateHasRun = true;
2272 120 : m_bDeferredCreateHasBeenSuccessful = false;
2273 :
2274 : try
2275 : {
2276 : // this driver enforces that all subdatasets are the same size
2277 240 : tiledb::Domain domain(*m_ctx);
2278 :
2279 120 : const uint64_t w = static_cast<uint64_t>(nBlocksX) * nBlockXSize - 1;
2280 120 : const uint64_t h = static_cast<uint64_t>(nBlocksY) * nBlockYSize - 1;
2281 :
2282 120 : auto d1 = tiledb::Dimension::create<uint64_t>(*m_ctx, "X", {0, w},
2283 483 : uint64_t(nBlockXSize));
2284 117 : auto d2 = tiledb::Dimension::create<uint64_t>(*m_ctx, "Y", {0, h},
2285 468 : uint64_t(nBlockYSize));
2286 :
2287 : {
2288 : CPLErr eErr;
2289 : // Only used for unit test purposes (to check ability of GDAL to read
2290 : // an arbitrary array)
2291 : const char *pszAttrName =
2292 117 : CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
2293 117 : if ((nBands == 0) || (eIndexMode == ATTRIBUTES))
2294 : {
2295 6 : eErr = AddDimensions(domain, pszAttrName, d2, d1, nullptr);
2296 : }
2297 : else
2298 : {
2299 : auto d3 = tiledb::Dimension::create<uint64_t>(
2300 222 : *m_ctx, "BANDS", {1, uint64_t(nBands)}, 1);
2301 111 : eErr = AddDimensions(domain, pszAttrName, d2, d1, &d3);
2302 : }
2303 117 : if (eErr != CE_None)
2304 0 : return false;
2305 : }
2306 :
2307 117 : m_schema->set_domain(domain).set_order(
2308 117 : {{TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR}});
2309 :
2310 : // register additional attributes to the pixel value, these will be
2311 : // be reported as subdatasets on future reads
2312 121 : for (const auto &poAttrDS : m_lpoAttributeDS)
2313 : {
2314 : const std::string osAttrName =
2315 4 : CPLGetBasenameSafe(poAttrDS->GetDescription());
2316 4 : GDALRasterBand *poAttrBand = poAttrDS->GetRasterBand(1);
2317 4 : int bHasNoData = false;
2318 4 : const double dfNoData = poAttrBand->GetNoDataValue(&bHasNoData);
2319 4 : CreateAttribute(poAttrBand->GetRasterDataType(), osAttrName.c_str(),
2320 4 : 1, CPL_TO_BOOL(bHasNoData), dfNoData);
2321 : }
2322 :
2323 117 : if (bCreateArray)
2324 : {
2325 116 : CreateArray();
2326 : }
2327 :
2328 117 : m_bDeferredCreateHasBeenSuccessful = true;
2329 117 : return true;
2330 : }
2331 3 : catch (const tiledb::TileDBError &e)
2332 : {
2333 3 : CPLError(CE_Failure, CPLE_AppDefined, "TileDB: %s", e.what());
2334 3 : return false;
2335 : }
2336 : }
2337 :
2338 : /************************************************************************/
2339 : /* CreateArray() */
2340 : /************************************************************************/
2341 :
2342 117 : void TileDBRasterDataset::CreateArray()
2343 : {
2344 117 : tiledb::Array::create(m_osArrayURI, *m_schema);
2345 :
2346 117 : if (m_bDatasetInGroup)
2347 : {
2348 321 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
2349 107 : group.add_member(m_osArrayURI, false);
2350 107 : group.close();
2351 : }
2352 :
2353 117 : if (nTimestamp)
2354 4 : m_array.reset(new tiledb::Array(
2355 2 : *m_ctx, m_osArrayURI, TILEDB_WRITE,
2356 6 : tiledb::TemporalPolicy(tiledb::TimeTravel, nTimestamp)));
2357 : else
2358 115 : m_array.reset(new tiledb::Array(*m_ctx, m_osArrayURI, TILEDB_WRITE));
2359 117 : }
2360 :
2361 : /************************************************************************/
2362 : /* CopySubDatasets() */
2363 : /* */
2364 : /* Copy SubDatasets from src to a TileDBRasterDataset */
2365 : /* */
2366 : /************************************************************************/
2367 :
2368 1 : CPLErr TileDBRasterDataset::CopySubDatasets(GDALDataset *poSrcDS,
2369 : TileDBRasterDataset *poDstDS,
2370 : GDALProgressFunc pfnProgress,
2371 : void *pProgressData)
2372 :
2373 : {
2374 : try
2375 : {
2376 2 : std::vector<std::unique_ptr<GDALDataset>> apoDatasets;
2377 1 : poDstDS->m_bHasSubDatasets = true;
2378 1 : CSLConstList papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
2379 1 : if (!papszSrcSubDatasets)
2380 0 : return CE_Failure;
2381 : const char *pszSubDSName =
2382 1 : CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
2383 1 : if (!pszSubDSName)
2384 0 : return CE_Failure;
2385 :
2386 : CPLStringList apszTokens(CSLTokenizeString2(
2387 2 : pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
2388 : // FIXME? this is tailored for HDF5-like subdataset names
2389 : // HDF5:foo.hdf5:attrname.
2390 1 : if (apszTokens.size() != 3)
2391 : {
2392 0 : CPLError(CE_Failure, CPLE_AppDefined,
2393 : "Cannot guess attribute name in %s", pszSubDSName);
2394 0 : return CE_Failure;
2395 : }
2396 :
2397 : std::unique_ptr<GDALDataset> poSubDataset(
2398 2 : GDALDataset::Open(pszSubDSName));
2399 2 : if (poSubDataset.get() == nullptr ||
2400 1 : poSubDataset->GetRasterCount() == 0)
2401 : {
2402 0 : return CE_Failure;
2403 : }
2404 :
2405 1 : uint64_t nSubXSize = poSubDataset->GetRasterXSize();
2406 1 : uint64_t nSubYSize = poSubDataset->GetRasterYSize();
2407 :
2408 1 : const char *pszAttrName = apszTokens[2];
2409 :
2410 1 : auto poFirstSubDSBand = poSubDataset->GetRasterBand(1);
2411 1 : int bFirstSubDSBandHasNoData = FALSE;
2412 : const double dfFirstSubDSBandNoData =
2413 1 : poFirstSubDSBand->GetNoDataValue(&bFirstSubDSBandHasNoData);
2414 1 : poDstDS->CreateAttribute(poFirstSubDSBand->GetRasterDataType(),
2415 : pszAttrName, poSubDataset->GetRasterCount(),
2416 1 : CPL_TO_BOOL(bFirstSubDSBandHasNoData),
2417 : dfFirstSubDSBandNoData);
2418 1 : apoDatasets.push_back(std::move(poSubDataset));
2419 :
2420 8 : for (const auto &[pszKey, pszValue] :
2421 9 : cpl::IterateNameValue(papszSrcSubDatasets))
2422 : {
2423 4 : if (EQUAL(pszKey, "SUBDATASET_1_NAME") || !strstr(pszKey, "_NAME"))
2424 : {
2425 3 : continue;
2426 : }
2427 1 : pszSubDSName = pszValue;
2428 : apszTokens = CSLTokenizeString2(
2429 1 : pszSubDSName, ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
2430 1 : if (apszTokens.size() != 3)
2431 : {
2432 0 : CPLError(CE_Failure, CPLE_AppDefined,
2433 : "Cannot guess attribute name in %s", pszSubDSName);
2434 0 : continue;
2435 : }
2436 :
2437 : std::unique_ptr<GDALDataset> poSubDS(
2438 2 : GDALDataset::Open(pszSubDSName));
2439 1 : if ((poSubDS != nullptr) && poSubDS->GetRasterCount() > 0)
2440 : {
2441 1 : GDALRasterBand *poBand = poSubDS->GetRasterBand(1);
2442 : int nBlockXSize, nBlockYSize;
2443 1 : poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
2444 :
2445 1 : int bHasNoData = FALSE;
2446 1 : const double dfNoData = poBand->GetNoDataValue(&bHasNoData);
2447 :
2448 1 : if ((poSubDS->GetRasterXSize() !=
2449 1 : static_cast<int>(nSubXSize)) ||
2450 1 : (poSubDS->GetRasterYSize() !=
2451 1 : static_cast<int>(nSubYSize)) ||
2452 3 : (nBlockXSize != poDstDS->nBlockXSize) ||
2453 1 : (nBlockYSize != poDstDS->nBlockYSize))
2454 : {
2455 0 : CPLError(CE_Warning, CPLE_AppDefined,
2456 : "Sub-datasets must have the same dimension,"
2457 : " and block sizes, skipping %s",
2458 : pszSubDSName);
2459 : }
2460 : else
2461 : {
2462 1 : pszAttrName = apszTokens[2];
2463 1 : poDstDS->CreateAttribute(
2464 : poSubDS->GetRasterBand(1)->GetRasterDataType(),
2465 : pszAttrName, poSubDS->GetRasterCount(),
2466 1 : CPL_TO_BOOL(bHasNoData), dfNoData);
2467 1 : apoDatasets.push_back(std::move(poSubDS));
2468 : }
2469 : }
2470 : else
2471 : {
2472 0 : CPLError(
2473 : CE_Warning, CPLE_AppDefined,
2474 : "Sub-datasets must be not null and contain data in bands,"
2475 : "skipping %s\n",
2476 : pszSubDSName);
2477 : }
2478 : }
2479 :
2480 1 : poDstDS->SetMetadata(poDstDS->m_aosSubdatasetMD.List(), "SUBDATASETS");
2481 :
2482 1 : poDstDS->CreateArray();
2483 :
2484 : /* -------------------------------------------------------- */
2485 : /* Report preliminary (0) progress. */
2486 : /* --------------------------------------------------------- */
2487 1 : if (!pfnProgress(0.0, nullptr, pProgressData))
2488 : {
2489 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
2490 : "User terminated CreateCopy()");
2491 0 : return CE_Failure;
2492 : }
2493 :
2494 : // copy over subdatasets by block
2495 2 : tiledb::Query query(*poDstDS->m_ctx, *poDstDS->m_array);
2496 1 : query.set_layout(TILEDB_GLOBAL_ORDER);
2497 1 : const uint64_t nTotalBlocks =
2498 1 : static_cast<uint64_t>(poDstDS->nBlocksX) * poDstDS->nBlocksY;
2499 1 : uint64_t nBlockCounter = 0;
2500 :
2501 : // row-major
2502 6 : for (int j = 0; j < poDstDS->nBlocksY; ++j)
2503 : {
2504 55 : for (int i = 0; i < poDstDS->nBlocksX; ++i)
2505 : {
2506 50 : std::vector<std::unique_ptr<void, decltype(&VSIFree)>> aBlocks;
2507 : // have to write set all tiledb attributes on write
2508 50 : int iAttr = 0;
2509 150 : for (auto &poSubDS : apoDatasets)
2510 : {
2511 : const GDALDataType eDT =
2512 100 : poSubDS->GetRasterBand(1)->GetRasterDataType();
2513 :
2514 200 : for (int b = 1; b <= poSubDS->GetRasterCount(); ++b)
2515 : {
2516 100 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
2517 100 : const size_t nValues =
2518 100 : static_cast<size_t>(poDstDS->nBlockXSize) *
2519 100 : poDstDS->nBlockYSize;
2520 100 : void *pBlock = VSI_MALLOC_VERBOSE(nDTSize * nValues);
2521 100 : if (!pBlock)
2522 0 : return CE_Failure;
2523 100 : aBlocks.emplace_back(pBlock, &VSIFree);
2524 100 : GDALRasterBand *poBand = poSubDS->GetRasterBand(b);
2525 100 : if (poBand->ReadBlock(i, j, pBlock) == CE_None)
2526 : {
2527 100 : SetBuffer(
2528 : &query, eDT,
2529 200 : poDstDS->m_schema->attribute(iAttr).name(),
2530 : pBlock, nValues);
2531 : }
2532 100 : ++iAttr;
2533 : }
2534 : }
2535 :
2536 50 : if (poDstDS->bStats)
2537 0 : tiledb::Stats::enable();
2538 :
2539 50 : auto status = query.submit();
2540 :
2541 50 : if (poDstDS->bStats)
2542 : {
2543 0 : tiledb::Stats::dump(stdout);
2544 0 : tiledb::Stats::disable();
2545 : }
2546 :
2547 50 : if (status == tiledb::Query::Status::FAILED)
2548 : {
2549 0 : return CE_Failure;
2550 : }
2551 :
2552 50 : ++nBlockCounter;
2553 50 : if (!pfnProgress(static_cast<double>(nBlockCounter) /
2554 50 : static_cast<double>(nTotalBlocks),
2555 : nullptr, pProgressData))
2556 : {
2557 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
2558 : "User terminated CreateCopy()");
2559 0 : return CE_Failure;
2560 : }
2561 : }
2562 : }
2563 :
2564 1 : query.finalize();
2565 :
2566 1 : return CE_None;
2567 : }
2568 0 : catch (const tiledb::TileDBError &e)
2569 : {
2570 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2571 0 : return CE_Failure;
2572 : }
2573 : }
2574 :
2575 : /************************************************************************/
2576 : /* Create() */
2577 : /************************************************************************/
2578 :
2579 119 : TileDBRasterDataset *TileDBRasterDataset::Create(const char *pszFilename,
2580 : int nXSize, int nYSize,
2581 : int nBandsIn,
2582 : GDALDataType eType,
2583 : char **papszOptions)
2584 :
2585 : {
2586 238 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
2587 :
2588 : std::unique_ptr<TileDBRasterDataset> poDS(TileDBRasterDataset::CreateLL(
2589 238 : osArrayPath, nXSize, nYSize, nBandsIn, eType, papszOptions));
2590 :
2591 119 : if (!poDS)
2592 3 : return nullptr;
2593 :
2594 : const char *pszAttrName =
2595 116 : CPLGetConfigOption("TILEDB_ATTRIBUTE", TILEDB_VALUES);
2596 314 : for (int i = 0; i < poDS->nBands; i++)
2597 : {
2598 198 : if (poDS->eIndexMode == ATTRIBUTES)
2599 24 : poDS->SetBand(
2600 : i + 1, new TileDBRasterBand(
2601 12 : poDS.get(), i + 1,
2602 24 : TILEDB_VALUES + CPLString().Printf("_%i", i + 1)));
2603 : else
2604 558 : poDS->SetBand(i + 1,
2605 372 : new TileDBRasterBand(poDS.get(), i + 1, pszAttrName));
2606 : }
2607 :
2608 : // TILEDB_WRITE_IMAGE_STRUCTURE=NO only used for unit test purposes (to
2609 : // check ability of GDAL to read an arbitrary array)
2610 116 : if (CPLTestBool(CPLGetConfigOption("TILEDB_WRITE_IMAGE_STRUCTURE", "YES")))
2611 : {
2612 204 : CPLStringList aosImageStruct;
2613 : aosImageStruct.SetNameValue(
2614 102 : "NBITS", CPLString().Printf("%d", poDS->nBitsPerSample));
2615 : aosImageStruct.SetNameValue(
2616 : "DATA_TYPE",
2617 102 : CPLString().Printf("%s", GDALGetDataTypeName(poDS->eDataType)));
2618 : aosImageStruct.SetNameValue(
2619 102 : "X_SIZE", CPLString().Printf("%d", poDS->nRasterXSize));
2620 : aosImageStruct.SetNameValue(
2621 102 : "Y_SIZE", CPLString().Printf("%d", poDS->nRasterYSize));
2622 : aosImageStruct.SetNameValue("INTERLEAVE",
2623 102 : index_type_name(poDS->eIndexMode));
2624 102 : aosImageStruct.SetNameValue("DATASET_TYPE", RASTER_DATASET_TYPE);
2625 :
2626 102 : if (poDS->m_lpoAttributeDS.size() > 0)
2627 : {
2628 2 : int i = 0;
2629 6 : for (auto const &poAttrDS : poDS->m_lpoAttributeDS)
2630 : {
2631 4 : aosImageStruct.SetNameValue(
2632 8 : CPLString().Printf("TILEDB_ATTRIBUTE_%i", ++i),
2633 12 : CPLGetBasenameSafe(poAttrDS->GetDescription()).c_str());
2634 : }
2635 : }
2636 102 : poDS->SetMetadata(aosImageStruct.List(), "IMAGE_STRUCTURE");
2637 : }
2638 :
2639 116 : return poDS.release();
2640 : }
2641 :
2642 : /************************************************************************/
2643 : /* CreateCopy() */
2644 : /************************************************************************/
2645 :
2646 52 : GDALDataset *TileDBRasterDataset::CreateCopy(const char *pszFilename,
2647 : GDALDataset *poSrcDS, int bStrict,
2648 : char **papszOptions,
2649 : GDALProgressFunc pfnProgress,
2650 : void *pProgressData)
2651 :
2652 : {
2653 104 : CPLStringList aosOptions(CSLDuplicate(papszOptions));
2654 104 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
2655 :
2656 52 : std::unique_ptr<TileDBRasterDataset> poDstDS;
2657 :
2658 52 : if (CSLFetchNameValue(papszOptions, "APPEND_SUBDATASET"))
2659 : {
2660 : // TileDB schemas are fixed
2661 0 : CPLError(CE_Failure, CPLE_NotSupported,
2662 : "TileDB driver does not support "
2663 : "appending to an existing schema.");
2664 0 : return nullptr;
2665 : }
2666 :
2667 52 : char **papszSrcSubDatasets = poSrcDS->GetMetadata("SUBDATASETS");
2668 :
2669 52 : if (papszSrcSubDatasets == nullptr)
2670 : {
2671 51 : const int nBands = poSrcDS->GetRasterCount();
2672 :
2673 51 : if (nBands > 0)
2674 : {
2675 51 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
2676 51 : GDALDataType eType = poBand->GetRasterDataType();
2677 :
2678 85 : for (int i = 2; i <= nBands; ++i)
2679 : {
2680 34 : if (eType != poSrcDS->GetRasterBand(i)->GetRasterDataType())
2681 : {
2682 0 : CPLError(CE_Failure, CPLE_NotSupported,
2683 : "TileDB driver does not support "
2684 : "source dataset with different band data types.");
2685 0 : return nullptr;
2686 : }
2687 : }
2688 :
2689 51 : poDstDS.reset(TileDBRasterDataset::Create(
2690 : osArrayPath, poSrcDS->GetRasterXSize(),
2691 : poSrcDS->GetRasterYSize(), nBands, eType, papszOptions));
2692 :
2693 51 : if (!poDstDS)
2694 : {
2695 3 : return nullptr;
2696 : }
2697 :
2698 130 : for (int i = 1; i <= nBands; ++i)
2699 : {
2700 82 : int bHasNoData = FALSE;
2701 : const double dfNoData =
2702 82 : poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
2703 82 : if (bHasNoData)
2704 3 : poDstDS->GetRasterBand(i)->SetNoDataValue(dfNoData);
2705 : }
2706 :
2707 : CPLErr eErr =
2708 48 : GDALDatasetCopyWholeRaster(poSrcDS, poDstDS.get(), papszOptions,
2709 : pfnProgress, pProgressData);
2710 :
2711 48 : if (eErr != CE_None)
2712 : {
2713 0 : CPLError(eErr, CPLE_AppDefined,
2714 : "Error copying raster to TileDB.");
2715 0 : return nullptr;
2716 : }
2717 : }
2718 : else
2719 : {
2720 0 : CPLError(CE_Failure, CPLE_NotSupported,
2721 : "TileDB driver does not support "
2722 : "source dataset with zero bands.");
2723 0 : return nullptr;
2724 : }
2725 : }
2726 : else
2727 : {
2728 1 : if (bStrict)
2729 : {
2730 0 : CPLError(CE_Failure, CPLE_NotSupported,
2731 : "TileDB driver does not support copying "
2732 : "subdatasets in strict mode.");
2733 0 : return nullptr;
2734 : }
2735 :
2736 2 : if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") ||
2737 1 : CSLFetchNameValue(papszOptions, "BLOCKYSIZE"))
2738 : {
2739 0 : CPLError(CE_Failure, CPLE_NotSupported,
2740 : "Changing block size is not supported when copying "
2741 : "subdatasets.");
2742 0 : return nullptr;
2743 : }
2744 :
2745 1 : const int nSubDatasetCount = CSLCount(papszSrcSubDatasets) / 2;
2746 : const int nMaxFiles =
2747 1 : atoi(CPLGetConfigOption("GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
2748 :
2749 1 : aosOptions.SetNameValue("CREATE_GROUP", "NO");
2750 :
2751 1 : if (nSubDatasetCount <= nMaxFiles)
2752 : {
2753 : const char *pszSource =
2754 1 : CSLFetchNameValue(papszSrcSubDatasets, "SUBDATASET_1_NAME");
2755 1 : if (pszSource)
2756 : {
2757 : std::unique_ptr<GDALDataset> poSubDataset(
2758 1 : GDALDataset::Open(pszSource));
2759 1 : if (poSubDataset && poSubDataset->GetRasterCount() > 0)
2760 : {
2761 1 : GDALRasterBand *poBand = poSubDataset->GetRasterBand(1);
2762 :
2763 1 : TileDBRasterDataset::SetBlockSize(poBand, aosOptions);
2764 1 : poDstDS.reset(TileDBRasterDataset::CreateLL(
2765 : osArrayPath, poBand->GetXSize(), poBand->GetYSize(), 0,
2766 1 : poBand->GetRasterDataType(), aosOptions.List()));
2767 :
2768 1 : if (poDstDS)
2769 : {
2770 1 : if (!poDstDS->DeferredCreate(
2771 : /* bCreateArray = */ false))
2772 0 : return nullptr;
2773 :
2774 1 : if (TileDBRasterDataset::CopySubDatasets(
2775 : poSrcDS, poDstDS.get(), pfnProgress,
2776 1 : pProgressData) != CE_None)
2777 : {
2778 0 : poDstDS.reset();
2779 0 : CPLError(CE_Failure, CPLE_AppDefined,
2780 : "Unable to copy subdatasets.");
2781 : }
2782 : }
2783 : }
2784 : }
2785 : }
2786 : else
2787 : {
2788 0 : CPLError(CE_Failure, CPLE_AppDefined,
2789 : "Please increase GDAL_READDIR_LIMIT_ON_OPEN variable.");
2790 : }
2791 : }
2792 :
2793 : // TODO Supporting mask bands is a possible future task
2794 49 : if (poDstDS != nullptr)
2795 : {
2796 49 : int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
2797 49 : poDstDS->CloneInfo(poSrcDS, nCloneFlags);
2798 :
2799 49 : if (poDstDS->FlushCache(false) != CE_None)
2800 : {
2801 0 : CPLError(CE_Failure, CPLE_AppDefined, "FlushCache() failed");
2802 0 : return nullptr;
2803 : }
2804 :
2805 49 : return poDstDS.release();
2806 : }
2807 0 : return nullptr;
2808 : }
2809 :
2810 : /************************************************************************/
2811 : /* LoadOverviews() */
2812 : /************************************************************************/
2813 :
2814 28 : void TileDBRasterDataset::LoadOverviews()
2815 : {
2816 28 : if (m_bLoadOverviewsDone)
2817 23 : return;
2818 5 : m_bLoadOverviewsDone = true;
2819 :
2820 : // NOTE: read overview_model.rst for a high level explanation of overviews
2821 : // are stored.
2822 :
2823 5 : if (!m_bDatasetInGroup)
2824 0 : return;
2825 :
2826 5 : CPLStringList aosOpenOptions;
2827 5 : if (nTimestamp)
2828 : {
2829 : aosOpenOptions.SetNameValue("TILEDB_TIMESTAMP",
2830 0 : CPLSPrintf("%" PRIu64, nTimestamp));
2831 : }
2832 5 : if (!m_osConfigFilename.empty())
2833 : {
2834 : aosOpenOptions.SetNameValue("TILEDB_CONFIG",
2835 0 : m_osConfigFilename.c_str());
2836 : }
2837 10 : for (int i = 0; i < m_nOverviewCountFromMetadata; ++i)
2838 : {
2839 5 : const std::string osArrayName = CPLSPrintf("l_%d", 1 + i);
2840 : const std::string osOvrDatasetName =
2841 5 : CPLFormFilenameSafe(GetDescription(), osArrayName.c_str(), nullptr);
2842 :
2843 5 : GDALOpenInfo oOpenInfo(osOvrDatasetName.c_str(), eAccess);
2844 5 : oOpenInfo.papszOpenOptions = aosOpenOptions.List();
2845 : auto poOvrDS = std::unique_ptr<GDALDataset>(
2846 5 : Open(&oOpenInfo, tiledb::Object::Type::Array));
2847 5 : if (!poOvrDS)
2848 0 : return;
2849 5 : if (poOvrDS->GetRasterCount() != nBands)
2850 : {
2851 0 : CPLError(CE_Failure, CPLE_AppDefined,
2852 : "Overview %s has not the same number of bands as full "
2853 : "resolution dataset",
2854 : osOvrDatasetName.c_str());
2855 0 : return;
2856 : }
2857 5 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
2858 : }
2859 : }
2860 :
2861 : /************************************************************************/
2862 : /* IBuildOverviews() */
2863 : /************************************************************************/
2864 :
2865 13 : CPLErr TileDBRasterDataset::IBuildOverviews(
2866 : const char *pszResampling, int nOverviews, const int *panOverviewList,
2867 : int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
2868 : void *pProgressData, CSLConstList papszOptions)
2869 : {
2870 : // NOTE: read overview_model.rst for a high level explanation of overviews
2871 : // are stored.
2872 :
2873 13 : if (eAccess == GA_ReadOnly)
2874 : {
2875 3 : if (!CPLTestBool(
2876 : CPLGetConfigOption("TILEDB_GEOTIFF_OVERVIEWS", "FALSE")))
2877 : {
2878 2 : ReportError(
2879 : CE_Failure, CPLE_NotSupported,
2880 : "Cannot %s overviews in TileDB format in read-only mode",
2881 : nOverviews ? "create" : "delete");
2882 2 : return CE_Failure;
2883 : }
2884 :
2885 : // GeoTIFF overviews. This used to be supported before GDAL 3.10
2886 : // although likely not desirable.
2887 1 : return GDALPamDataset::IBuildOverviews(
2888 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2889 1 : pfnProgress, pProgressData, papszOptions);
2890 : }
2891 :
2892 10 : if (nBands == 0)
2893 : {
2894 0 : return CE_Failure;
2895 : }
2896 :
2897 : // If we already have PAM overview (i.e. GeoTIFF based), go through PAM
2898 10 : if (cpl::down_cast<GDALPamRasterBand *>(GetRasterBand(1))
2899 10 : ->GDALPamRasterBand::GetOverviewCount() > 0)
2900 : {
2901 1 : return GDALPamDataset::IBuildOverviews(
2902 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2903 1 : pfnProgress, pProgressData, papszOptions);
2904 : }
2905 :
2906 9 : if (!m_bDatasetInGroup)
2907 : {
2908 2 : ReportError(CE_Failure, CPLE_NotSupported,
2909 : "IBuildOverviews() only supported for datasets created "
2910 : "with CREATE_GROUP=YES");
2911 2 : return CE_Failure;
2912 : }
2913 :
2914 : /* -------------------------------------------------------------------- */
2915 : /* Our overview support currently only works safely if all */
2916 : /* bands are handled at the same time. */
2917 : /* -------------------------------------------------------------------- */
2918 7 : if (nListBands != nBands)
2919 : {
2920 0 : ReportError(CE_Failure, CPLE_NotSupported,
2921 : "Generation of TileDB overviews currently only "
2922 : "supported when operating on all bands. "
2923 : "Operation failed.");
2924 0 : return CE_Failure;
2925 : }
2926 :
2927 : // Force loading existing overviews
2928 7 : if (m_nOverviewCountFromMetadata)
2929 4 : GetRasterBand(1)->GetOverviewCount();
2930 7 : m_bLoadOverviewsDone = true;
2931 :
2932 : /* -------------------------------------------------------------------- */
2933 : /* Deletes existing overviews if requested. */
2934 : /* -------------------------------------------------------------------- */
2935 7 : if (nOverviews == 0)
2936 : {
2937 2 : CPLErr eErr = CE_None;
2938 :
2939 : // Unlink arrays from he group
2940 : try
2941 : {
2942 6 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
2943 4 : for (auto &&poODS : m_apoOverviewDS)
2944 : {
2945 2 : group.remove_member(poODS->GetDescription());
2946 : }
2947 2 : group.close();
2948 : }
2949 0 : catch (const tiledb::TileDBError &e)
2950 : {
2951 0 : eErr = CE_Failure;
2952 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2953 : }
2954 :
2955 2 : tiledb::VFS vfs(*m_ctx, m_ctx->config());
2956 :
2957 : // Delete arrays
2958 4 : for (auto &&poODS : m_apoOverviewDS)
2959 : {
2960 : try
2961 : {
2962 2 : CPL_IGNORE_RET_VAL(poODS->Close());
2963 2 : tiledb::Array::delete_array(*m_ctx, poODS->GetDescription());
2964 2 : if (vfs.is_dir(poODS->GetDescription()))
2965 : {
2966 2 : vfs.remove_dir(poODS->GetDescription());
2967 : }
2968 : }
2969 0 : catch (const tiledb::TileDBError &e)
2970 : {
2971 0 : eErr = CE_Failure;
2972 0 : CPLError(CE_Failure, CPLE_AppDefined,
2973 : "Array::delete_array(%s) failed: %s",
2974 0 : poODS->GetDescription(), e.what());
2975 : }
2976 2 : m_apoOverviewDSRemoved.emplace_back(std::move(poODS));
2977 : }
2978 :
2979 2 : m_apoOverviewDS.clear();
2980 2 : m_nOverviewCountFromMetadata = 0;
2981 2 : MarkPamDirty();
2982 2 : return eErr;
2983 : }
2984 :
2985 : /* -------------------------------------------------------------------- */
2986 : /* Establish which of the overview levels we already have, and */
2987 : /* which are new. */
2988 : /* -------------------------------------------------------------------- */
2989 10 : std::vector<bool> abRequireNewOverview(nOverviews, true);
2990 10 : for (int i = 0; i < nOverviews; ++i)
2991 : {
2992 5 : const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), panOverviewList[i]);
2993 5 : const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), panOverviewList[i]);
2994 :
2995 6 : for (const auto &poODS : m_apoOverviewDS)
2996 : {
2997 : const int nOvFactor =
2998 2 : GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(),
2999 : poODS->GetRasterYSize(), GetRasterYSize());
3000 :
3001 : // If we already have a 1x1 overview and this new one would result
3002 : // in it too, then don't create it.
3003 2 : if (poODS->GetRasterXSize() == 1 && poODS->GetRasterYSize() == 1 &&
3004 2 : nOXSize == 1 && nOYSize == 1)
3005 : {
3006 0 : abRequireNewOverview[i] = false;
3007 0 : break;
3008 : }
3009 :
3010 3 : if (nOvFactor == panOverviewList[i] ||
3011 1 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3012 : GetRasterXSize(),
3013 : GetRasterYSize()))
3014 : {
3015 1 : abRequireNewOverview[i] = false;
3016 1 : break;
3017 : }
3018 : }
3019 :
3020 5 : if (abRequireNewOverview[i])
3021 : {
3022 4 : CPLStringList aosCreationOptions;
3023 4 : aosCreationOptions.SetNameValue("CREATE_GROUP", "NO");
3024 : aosCreationOptions.SetNameValue(
3025 4 : "NBITS", CPLString().Printf("%d", nBitsPerSample));
3026 : aosCreationOptions.SetNameValue("INTERLEAVE",
3027 4 : index_type_name(eIndexMode));
3028 4 : if (nTimestamp)
3029 : {
3030 : aosCreationOptions.SetNameValue(
3031 0 : "TILEDB_TIMESTAMP", CPLSPrintf("%" PRIu64, nTimestamp));
3032 : }
3033 4 : if (!m_osConfigFilename.empty())
3034 : {
3035 : aosCreationOptions.SetNameValue("TILEDB_CONFIG",
3036 0 : m_osConfigFilename.c_str());
3037 : }
3038 :
3039 : const std::string osArrayName =
3040 4 : CPLSPrintf("l_%d", 1 + int(m_apoOverviewDS.size()));
3041 : const std::string osOvrDatasetName = CPLFormFilenameSafe(
3042 4 : GetDescription(), osArrayName.c_str(), nullptr);
3043 :
3044 : auto poOvrDS = std::unique_ptr<TileDBRasterDataset>(
3045 : Create(osOvrDatasetName.c_str(), nOXSize, nOYSize, nBands,
3046 : GetRasterBand(1)->GetRasterDataType(),
3047 4 : aosCreationOptions.List()));
3048 4 : if (!poOvrDS)
3049 0 : return CE_Failure;
3050 :
3051 : // Apply nodata from main dataset
3052 16 : for (int j = 0; j < nBands; ++j)
3053 : {
3054 12 : int bHasNoData = FALSE;
3055 : const double dfNoData =
3056 12 : GetRasterBand(j + 1)->GetNoDataValue(&bHasNoData);
3057 12 : if (bHasNoData)
3058 12 : poOvrDS->GetRasterBand(j + 1)->SetNoDataValue(dfNoData);
3059 : }
3060 :
3061 : // Apply georeferencing from main dataset
3062 4 : poOvrDS->SetSpatialRef(GetSpatialRef());
3063 4 : GDALGeoTransform gt;
3064 4 : if (GetGeoTransform(gt) == CE_None)
3065 : {
3066 4 : gt.Rescale(static_cast<double>(nRasterXSize) / nOXSize,
3067 4 : static_cast<double>(nRasterYSize) / nOYSize);
3068 4 : poOvrDS->SetGeoTransform(gt);
3069 : }
3070 :
3071 4 : poOvrDS->DeferredCreate(/* bCreateArray = */ true);
3072 :
3073 : try
3074 : {
3075 12 : tiledb::Group group(*m_ctx, GetDescription(), TILEDB_WRITE);
3076 4 : group.add_member(osOvrDatasetName, false);
3077 4 : group.close();
3078 : }
3079 0 : catch (const tiledb::TileDBError &e)
3080 : {
3081 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
3082 : }
3083 :
3084 4 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
3085 : }
3086 : }
3087 :
3088 5 : m_nOverviewCountFromMetadata = static_cast<int>(m_apoOverviewDS.size());
3089 5 : MarkPamDirty();
3090 :
3091 : /* -------------------------------------------------------------------- */
3092 : /* Refresh/generate overviews that are listed. */
3093 : /* -------------------------------------------------------------------- */
3094 10 : std::vector<GDALRasterBand *> apoSrcBands;
3095 10 : std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands;
3096 5 : CPLErr eErr = CE_None;
3097 : const auto osNormalizedResampling =
3098 5 : GDALGetNormalizedOvrResampling(pszResampling);
3099 20 : for (int iBand = 0; eErr == CE_None && iBand < nBands; iBand++)
3100 : {
3101 15 : apoSrcBands.push_back(GetRasterBand(iBand + 1));
3102 30 : std::vector<GDALRasterBand *> apoOverviewBands;
3103 :
3104 : std::vector<bool> abAlreadyUsedOverviewBand(m_apoOverviewDS.size(),
3105 30 : false);
3106 :
3107 30 : for (int i = 0; i < nOverviews; i++)
3108 : {
3109 15 : bool bFound = false;
3110 18 : for (size_t j = 0; j < m_apoOverviewDS.size(); ++j)
3111 : {
3112 18 : if (!abAlreadyUsedOverviewBand[j])
3113 : {
3114 18 : auto &poODS = m_apoOverviewDS[j];
3115 18 : int nOvFactor = GDALComputeOvFactor(
3116 : poODS->GetRasterXSize(), nRasterXSize,
3117 : poODS->GetRasterYSize(), nRasterYSize);
3118 :
3119 21 : if (nOvFactor == panOverviewList[i] ||
3120 3 : nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
3121 : nRasterXSize,
3122 : nRasterYSize))
3123 : {
3124 15 : abAlreadyUsedOverviewBand[j] = true;
3125 15 : auto poOvrBand = poODS->GetRasterBand(iBand + 1);
3126 15 : if (!osNormalizedResampling.empty())
3127 : {
3128 : // Store resampling method in band metadata, as it
3129 : // can be used by the gdaladdo utilities to refresh
3130 : // existing overviews with the method previously
3131 : // used
3132 15 : poOvrBand->SetMetadataItem(
3133 15 : "RESAMPLING", osNormalizedResampling.c_str());
3134 : }
3135 15 : apoOverviewBands.push_back(poOvrBand);
3136 15 : bFound = true;
3137 15 : break;
3138 : }
3139 : }
3140 : }
3141 15 : if (!bFound)
3142 : {
3143 0 : CPLError(CE_Failure, CPLE_AppDefined,
3144 : "Could not find dataset corresponding to ov factor %d",
3145 0 : panOverviewList[i]);
3146 0 : eErr = CE_Failure;
3147 : }
3148 : }
3149 15 : if (iBand > 0)
3150 : {
3151 10 : CPLAssert(apoOverviewBands.size() == aapoOverviewBands[0].size());
3152 : }
3153 15 : aapoOverviewBands.emplace_back(std::move(apoOverviewBands));
3154 : }
3155 :
3156 5 : if (eErr == CE_None)
3157 : {
3158 5 : eErr = GDALRegenerateOverviewsMultiBand(apoSrcBands, aapoOverviewBands,
3159 : pszResampling, pfnProgress,
3160 : pProgressData, papszOptions);
3161 : }
3162 :
3163 5 : return eErr;
3164 : }
|