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