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) 2023, TileDB, Inc
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "tiledbheaders.h"
14 : #include "tiledbdrivercore.h"
15 :
16 : TileDBDataset::~TileDBDataset() = default;
17 :
18 : /************************************************************************/
19 : /* VSI_to_tiledb_uri() */
20 : /************************************************************************/
21 :
22 1957 : CPLString TileDBDataset::VSI_to_tiledb_uri(const char *pszUri)
23 : {
24 1957 : CPLString osUri;
25 :
26 1957 : if (STARTS_WITH_CI(pszUri, "/VSIS3/"))
27 0 : osUri.Printf("s3://%s", pszUri + 7);
28 1957 : else if (STARTS_WITH_CI(pszUri, "/VSIGS/"))
29 0 : osUri.Printf("gcs://%s", pszUri + 7);
30 1957 : else if (STARTS_WITH_CI(pszUri, "/VSIAZ/"))
31 0 : osUri.Printf("azure://%s", pszUri + 7);
32 : else
33 : {
34 1957 : osUri = pszUri;
35 : // tiledb (at least at 2.4.2 on Conda) wrongly interprets relative
36 : // directories on Windows as absolute ones.
37 1957 : if (CPLIsFilenameRelative(pszUri))
38 : {
39 940 : char *pszCurDir = CPLGetCurrentDir();
40 940 : if (pszCurDir)
41 940 : osUri = CPLFormFilenameSafe(pszCurDir, pszUri, nullptr);
42 940 : CPLFree(pszCurDir);
43 : }
44 : }
45 :
46 1957 : return osUri;
47 : }
48 :
49 : /************************************************************************/
50 : /* TileDBObjectExists() */
51 : /************************************************************************/
52 1580 : bool TileDBDataset::TileDBObjectExists(tiledb::Context &ctx,
53 : const std::string &osArrayUri)
54 : {
55 1580 : const auto eType = tiledb::Object::object(ctx, osArrayUri).type();
56 1580 : return eType != tiledb::Object::Type::Invalid;
57 : }
58 :
59 : /************************************************************************/
60 : /* AddFilter() */
61 : /************************************************************************/
62 :
63 2 : CPLErr TileDBDataset::AddFilter(tiledb::Context &ctx,
64 : tiledb::FilterList &filterList,
65 : const char *pszFilterName, const int level)
66 :
67 : {
68 : try
69 : {
70 2 : if (pszFilterName == nullptr)
71 : filterList.add_filter(
72 0 : tiledb::Filter(ctx, TILEDB_FILTER_NONE)
73 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
74 2 : else if EQUAL (pszFilterName, "GZIP")
75 : filterList.add_filter(
76 0 : tiledb::Filter(ctx, TILEDB_FILTER_GZIP)
77 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
78 2 : else if EQUAL (pszFilterName, "ZSTD")
79 : filterList.add_filter(
80 4 : tiledb::Filter(ctx, TILEDB_FILTER_ZSTD)
81 2 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
82 0 : else if EQUAL (pszFilterName, "LZ4")
83 : filterList.add_filter(
84 0 : tiledb::Filter(ctx, TILEDB_FILTER_LZ4)
85 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
86 0 : else if EQUAL (pszFilterName, "RLE")
87 : filterList.add_filter(
88 0 : tiledb::Filter(ctx, TILEDB_FILTER_RLE)
89 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
90 0 : else if EQUAL (pszFilterName, "BZIP2")
91 : filterList.add_filter(
92 0 : tiledb::Filter(ctx, TILEDB_FILTER_BZIP2)
93 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
94 0 : else if EQUAL (pszFilterName, "DOUBLE-DELTA")
95 : filterList.add_filter(
96 0 : tiledb::Filter(ctx, TILEDB_FILTER_DOUBLE_DELTA));
97 0 : else if EQUAL (pszFilterName, "POSITIVE-DELTA")
98 : filterList.add_filter(
99 0 : tiledb::Filter(ctx, TILEDB_FILTER_POSITIVE_DELTA));
100 : else
101 0 : return CE_Failure;
102 :
103 2 : return CE_None;
104 : }
105 0 : catch (const tiledb::TileDBError &e)
106 : {
107 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
108 0 : return CE_Failure;
109 : }
110 : }
111 :
112 : /************************************************************************/
113 : /* Identify() */
114 : /************************************************************************/
115 :
116 51964 : int TileDBDataset::Identify(GDALOpenInfo *poOpenInfo)
117 :
118 : {
119 51964 : int nRet = TileDBDriverIdentifySimplified(poOpenInfo);
120 51964 : if (nRet == GDAL_IDENTIFY_UNKNOWN)
121 : {
122 : try
123 : {
124 2506 : const char *pszConfig = CSLFetchNameValue(
125 1253 : poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
126 2506 : tiledb::Context oCtx;
127 :
128 1253 : if (pszConfig != nullptr)
129 : {
130 0 : tiledb::Config cfg(pszConfig);
131 0 : oCtx = tiledb::Context(cfg);
132 : }
133 : else
134 : {
135 1253 : tiledb::Config cfg;
136 1253 : cfg["sm.enable_signal_handlers"] = "false";
137 1253 : oCtx = tiledb::Context(cfg);
138 : }
139 :
140 : CPLString osArrayPath =
141 2506 : TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
142 1253 : if (TileDBDataset::TileDBObjectExists(oCtx, osArrayPath))
143 377 : nRet = TRUE;
144 : else
145 876 : nRet = FALSE;
146 : }
147 0 : catch (...)
148 : {
149 0 : nRet = FALSE;
150 : }
151 : }
152 51964 : return nRet;
153 : }
154 :
155 : /************************************************************************/
156 : /* Delete() */
157 : /************************************************************************/
158 :
159 16 : CPLErr TileDBDataset::Delete(const char *pszFilename)
160 :
161 : {
162 : try
163 : {
164 32 : tiledb::Context ctx;
165 32 : tiledb::VFS vfs(ctx);
166 32 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
167 :
168 16 : if (vfs.is_dir(osArrayPath))
169 : {
170 0 : vfs.remove_dir(osArrayPath);
171 0 : return CE_None;
172 : }
173 : else
174 16 : return CE_Failure;
175 : }
176 0 : catch (const tiledb::TileDBError &e)
177 : {
178 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
179 0 : return CE_Failure;
180 : }
181 : }
182 :
183 : /************************************************************************/
184 : /* Open() */
185 : /************************************************************************/
186 :
187 252 : GDALDataset *TileDBDataset::Open(GDALOpenInfo *poOpenInfo)
188 :
189 : {
190 : try
191 : {
192 252 : const auto eIdentify = TileDBDataset::Identify(poOpenInfo);
193 252 : if (eIdentify == GDAL_IDENTIFY_FALSE)
194 5 : return nullptr;
195 :
196 247 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
197 2 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
198 : {
199 : // subdataset URI so this is a raster
200 2 : return TileDBRasterDataset::Open(poOpenInfo,
201 2 : tiledb::Object::Type::Invalid);
202 : }
203 : else
204 : {
205 : const std::string osPath =
206 490 : TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
207 :
208 490 : const char *pszConfig = CSLFetchNameValue(
209 245 : poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
210 490 : tiledb::Context oCtx;
211 :
212 245 : if (pszConfig != nullptr)
213 : {
214 0 : tiledb::Config cfg(pszConfig);
215 0 : oCtx = tiledb::Context(cfg);
216 : }
217 : else
218 : {
219 245 : tiledb::Config cfg;
220 245 : cfg["sm.enable_signal_handlers"] = "false";
221 245 : oCtx = tiledb::Context(cfg);
222 : }
223 :
224 : // identify() identifies this as a request for a TileDB dataset but not whether it exists
225 466 : if ((poOpenInfo->eAccess == GA_ReadOnly) &&
226 221 : (!TileDBDataset::TileDBObjectExists(oCtx, osPath)))
227 : {
228 55 : CPLError(CE_Failure, CPLE_OpenFailed,
229 : "Failed to open %s as an array or group.",
230 : poOpenInfo->pszFilename);
231 55 : return nullptr;
232 : }
233 :
234 190 : if ((poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
235 : {
236 29 : return TileDBDataset::OpenMultiDimensional(poOpenInfo);
237 : }
238 :
239 161 : const auto eType = tiledb::Object::object(oCtx, osPath).type();
240 322 : std::string osDatasetType;
241 161 : if (eType == tiledb::Object::Type::Group)
242 : {
243 240 : tiledb::Group group(oCtx, osPath, TILEDB_READ);
244 120 : tiledb_datatype_t v_type = TILEDB_UINT8;
245 120 : const void *v_r = nullptr;
246 120 : uint32_t v_num = 0;
247 120 : group.get_metadata(DATASET_TYPE_ATTRIBUTE_NAME, &v_type, &v_num,
248 : &v_r);
249 116 : if (v_r && (v_type == TILEDB_UINT8 || v_type == TILEDB_CHAR ||
250 116 : v_type == TILEDB_STRING_ASCII ||
251 236 : v_type == TILEDB_STRING_UTF8))
252 : {
253 : osDatasetType =
254 116 : std::string(static_cast<const char *>(v_r), v_num);
255 : }
256 : }
257 :
258 159 : if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
259 436 : eType == tiledb::Object::Type::Group &&
260 235 : (osDatasetType.empty() ||
261 116 : osDatasetType == GEOMETRY_DATASET_TYPE))
262 : {
263 3 : return OGRTileDBDataset::Open(poOpenInfo, eType);
264 : }
265 452 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
266 136 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0 &&
267 294 : eType == tiledb::Object::Type::Group &&
268 1 : osDatasetType == GEOMETRY_DATASET_TYPE)
269 : {
270 0 : return nullptr;
271 : }
272 136 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
273 294 : eType == tiledb::Object::Type::Group &&
274 117 : osDatasetType == RASTER_DATASET_TYPE)
275 : {
276 116 : return TileDBRasterDataset::Open(poOpenInfo, eType);
277 : }
278 124 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
279 40 : (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
280 82 : eType == tiledb::Object::Type::Group &&
281 0 : osDatasetType == RASTER_DATASET_TYPE)
282 : {
283 0 : return nullptr;
284 : }
285 20 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
286 62 : eType == tiledb::Object::Type::Group &&
287 1 : osDatasetType.empty())
288 : {
289 : // Compatibility with generic arrays
290 : // If this is a group which has only a single 2D array and
291 : // no 3D+ arrays, then return this 2D array.
292 : auto poDSUnique = std::unique_ptr<GDALDataset>(
293 2 : TileDBDataset::OpenMultiDimensional(poOpenInfo));
294 1 : if (poDSUnique)
295 : {
296 1 : auto poRootGroup = poDSUnique->GetRootGroup();
297 1 : if (poRootGroup && poRootGroup->GetGroupNames().empty())
298 : {
299 0 : std::shared_ptr<GDALMDArray> poCandidateArray;
300 4 : for (const auto &osName :
301 9 : poRootGroup->GetMDArrayNames())
302 : {
303 4 : auto poArray = poRootGroup->OpenMDArray(osName);
304 4 : if (poArray && poArray->GetDimensionCount() >= 3)
305 : {
306 0 : poCandidateArray.reset();
307 0 : break;
308 : }
309 8 : else if (poArray &&
310 9 : poArray->GetDimensionCount() == 2 &&
311 1 : poArray->GetDimensions()[0]->GetType() ==
312 8 : GDAL_DIM_TYPE_HORIZONTAL_Y &&
313 1 : poArray->GetDimensions()[1]->GetType() ==
314 : GDAL_DIM_TYPE_HORIZONTAL_X)
315 : {
316 1 : if (!poCandidateArray)
317 : {
318 1 : poCandidateArray = std::move(poArray);
319 : }
320 : else
321 : {
322 0 : poCandidateArray.reset();
323 0 : break;
324 : }
325 : }
326 : }
327 1 : if (poCandidateArray)
328 : {
329 1 : return poCandidateArray->AsClassicDataset(1, 0);
330 : }
331 : }
332 : }
333 0 : return nullptr;
334 : }
335 :
336 82 : tiledb::ArraySchema schema(oCtx, osPath);
337 :
338 41 : if (schema.array_type() == TILEDB_SPARSE)
339 35 : return OGRTileDBDataset::Open(poOpenInfo, eType);
340 : else
341 6 : return TileDBRasterDataset::Open(poOpenInfo, eType);
342 : }
343 : }
344 0 : catch (const tiledb::TileDBError &e)
345 : {
346 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
347 0 : return nullptr;
348 : }
349 : }
350 :
351 : /************************************************************************/
352 : /* Create() */
353 : /************************************************************************/
354 :
355 114 : GDALDataset *TileDBDataset::Create(const char *pszFilename, int nXSize,
356 : int nYSize, int nBandsIn, GDALDataType eType,
357 : CSLConstList papszOptions)
358 :
359 : {
360 : try
361 : {
362 114 : if (nBandsIn > 0)
363 64 : return TileDBRasterDataset::Create(pszFilename, nXSize, nYSize,
364 64 : nBandsIn, eType, papszOptions);
365 : else
366 50 : return OGRTileDBDataset::Create(pszFilename, papszOptions);
367 : }
368 0 : catch (const tiledb::TileDBError &e)
369 : {
370 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
371 : }
372 :
373 0 : return nullptr;
374 : }
375 :
376 : /************************************************************************/
377 : /* CreateCopy() */
378 : /************************************************************************/
379 :
380 54 : GDALDataset *TileDBDataset::CreateCopy(const char *pszFilename,
381 : GDALDataset *poSrcDS, int bStrict,
382 : CSLConstList papszOptions,
383 : GDALProgressFunc pfnProgress,
384 : void *pProgressData)
385 :
386 : {
387 54 : if (poSrcDS->GetRootGroup())
388 : {
389 1 : auto poDrv = GDALDriver::FromHandle(GDALGetDriverByName("TileDB"));
390 1 : if (poDrv)
391 : {
392 1 : return poDrv->DefaultCreateCopy(pszFilename, poSrcDS, bStrict,
393 : papszOptions, pfnProgress,
394 1 : pProgressData);
395 : }
396 : }
397 :
398 : try
399 : {
400 55 : if (poSrcDS->GetRasterCount() > 0 ||
401 2 : poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS))
402 : {
403 52 : return TileDBRasterDataset::CreateCopy(pszFilename, poSrcDS,
404 : bStrict, papszOptions,
405 52 : pfnProgress, pProgressData);
406 : }
407 : }
408 0 : catch (const tiledb::TileDBError &e)
409 : {
410 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
411 : }
412 :
413 1 : return nullptr;
414 : }
415 :
416 : /************************************************************************/
417 : /* GDALRegister_TILEDB() */
418 : /************************************************************************/
419 :
420 27 : void GDALRegister_TileDB()
421 :
422 : {
423 27 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
424 0 : return;
425 :
426 27 : GDALDriver *poDriver = new GDALDriver();
427 27 : TileDBDriverSetCommonMetadata(poDriver);
428 :
429 27 : poDriver->pfnIdentify = TileDBDataset::Identify;
430 27 : poDriver->pfnOpen = TileDBDataset::Open;
431 27 : poDriver->pfnCreate = TileDBDataset::Create;
432 27 : poDriver->pfnCreateCopy = TileDBDataset::CreateCopy;
433 27 : poDriver->pfnDelete = TileDBDataset::Delete;
434 27 : poDriver->pfnCreateMultiDimensional = TileDBDataset::CreateMultiDimensional;
435 :
436 27 : GetGDALDriverManager()->RegisterDriver(poDriver);
437 : }
|