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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "tiledbheaders.h"
30 : #include "tiledbdrivercore.h"
31 :
32 : /************************************************************************/
33 : /* VSI_to_tiledb_uri() */
34 : /************************************************************************/
35 :
36 1365 : CPLString TileDBDataset::VSI_to_tiledb_uri(const char *pszUri)
37 : {
38 1365 : CPLString osUri;
39 :
40 1365 : if (STARTS_WITH_CI(pszUri, "/VSIS3/"))
41 0 : osUri.Printf("s3://%s", pszUri + 7);
42 1365 : else if (STARTS_WITH_CI(pszUri, "/VSIGS/"))
43 0 : osUri.Printf("gcs://%s", pszUri + 7);
44 : else
45 : {
46 1365 : osUri = pszUri;
47 : // tiledb (at least at 2.4.2 on Conda) wrongly interprets relative
48 : // directories on Windows as absolute ones.
49 1365 : if (CPLIsFilenameRelative(pszUri))
50 : {
51 982 : char *pszCurDir = CPLGetCurrentDir();
52 982 : if (pszCurDir)
53 982 : osUri = CPLFormFilename(pszCurDir, pszUri, nullptr);
54 982 : CPLFree(pszCurDir);
55 : }
56 : }
57 :
58 1365 : return osUri;
59 : }
60 :
61 : /************************************************************************/
62 : /* AddFilter() */
63 : /************************************************************************/
64 :
65 2 : CPLErr TileDBDataset::AddFilter(tiledb::Context &ctx,
66 : tiledb::FilterList &filterList,
67 : const char *pszFilterName, const int level)
68 :
69 : {
70 : try
71 : {
72 2 : if (pszFilterName == nullptr)
73 : filterList.add_filter(
74 0 : tiledb::Filter(ctx, TILEDB_FILTER_NONE)
75 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
76 2 : else if EQUAL (pszFilterName, "GZIP")
77 : filterList.add_filter(
78 0 : tiledb::Filter(ctx, TILEDB_FILTER_GZIP)
79 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
80 2 : else if EQUAL (pszFilterName, "ZSTD")
81 : filterList.add_filter(
82 4 : tiledb::Filter(ctx, TILEDB_FILTER_ZSTD)
83 2 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
84 0 : else if EQUAL (pszFilterName, "LZ4")
85 : filterList.add_filter(
86 0 : tiledb::Filter(ctx, TILEDB_FILTER_LZ4)
87 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
88 0 : else if EQUAL (pszFilterName, "RLE")
89 : filterList.add_filter(
90 0 : tiledb::Filter(ctx, TILEDB_FILTER_RLE)
91 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
92 0 : else if EQUAL (pszFilterName, "BZIP2")
93 : filterList.add_filter(
94 0 : tiledb::Filter(ctx, TILEDB_FILTER_BZIP2)
95 0 : .set_option(TILEDB_COMPRESSION_LEVEL, level));
96 0 : else if EQUAL (pszFilterName, "DOUBLE-DELTA")
97 : filterList.add_filter(
98 0 : tiledb::Filter(ctx, TILEDB_FILTER_DOUBLE_DELTA));
99 0 : else if EQUAL (pszFilterName, "POSITIVE-DELTA")
100 : filterList.add_filter(
101 0 : tiledb::Filter(ctx, TILEDB_FILTER_POSITIVE_DELTA));
102 : else
103 0 : return CE_Failure;
104 :
105 2 : return CE_None;
106 : }
107 0 : catch (const tiledb::TileDBError &e)
108 : {
109 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
110 0 : return CE_Failure;
111 : }
112 : }
113 :
114 : /************************************************************************/
115 : /* Identify() */
116 : /************************************************************************/
117 :
118 34518 : int TileDBDataset::Identify(GDALOpenInfo *poOpenInfo)
119 :
120 : {
121 34518 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:"))
122 : {
123 4 : return TRUE;
124 : }
125 :
126 : try
127 : {
128 : const char *pszConfig =
129 34514 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
130 :
131 34510 : if (pszConfig != nullptr)
132 : {
133 0 : return TRUE;
134 : }
135 :
136 34510 : const bool bIsS3OrGS =
137 69033 : STARTS_WITH_CI(poOpenInfo->pszFilename, "/VSIS3/") ||
138 34523 : STARTS_WITH_CI(poOpenInfo->pszFilename, "/VSIGS/");
139 : // If this is a /vsi virtual file systems, bail out, except if it is S3 or GS.
140 34510 : if (!bIsS3OrGS && STARTS_WITH(poOpenInfo->pszFilename, "/vsi"))
141 : {
142 14265 : return false;
143 : }
144 :
145 20248 : if (poOpenInfo->bIsDirectory ||
146 3 : (bIsS3OrGS &&
147 3 : !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "tif")))
148 : {
149 826 : tiledb::Context ctx;
150 : CPLString osArrayPath =
151 826 : TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
152 826 : const auto eType = tiledb::Object::object(ctx, osArrayPath).type();
153 826 : if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
154 : {
155 502 : if (eType == tiledb::Object::Type::Array ||
156 : eType == tiledb::Object::Type::Group)
157 119 : return true;
158 : }
159 :
160 707 : if ((poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
161 : {
162 121 : if (eType == tiledb::Object::Type::Array ||
163 : eType == tiledb::Object::Type::Group)
164 52 : return true;
165 : }
166 655 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
167 : {
168 216 : if (eType == tiledb::Object::Type::Group)
169 2 : return GDAL_IDENTIFY_UNKNOWN;
170 214 : return eType == tiledb::Object::Type::Array;
171 : }
172 : }
173 :
174 19858 : return FALSE;
175 : }
176 0 : catch (...)
177 : {
178 0 : return FALSE;
179 : }
180 : }
181 :
182 : /************************************************************************/
183 : /* Delete() */
184 : /************************************************************************/
185 :
186 48 : CPLErr TileDBDataset::Delete(const char *pszFilename)
187 :
188 : {
189 : try
190 : {
191 96 : tiledb::Context ctx;
192 96 : tiledb::VFS vfs(ctx);
193 96 : CPLString osArrayPath = TileDBDataset::VSI_to_tiledb_uri(pszFilename);
194 :
195 48 : if (vfs.is_dir(osArrayPath))
196 : {
197 32 : vfs.remove_dir(osArrayPath);
198 32 : return CE_None;
199 : }
200 : else
201 16 : return CE_Failure;
202 : }
203 0 : catch (const tiledb::TileDBError &e)
204 : {
205 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
206 0 : return CE_Failure;
207 : }
208 : }
209 :
210 : /************************************************************************/
211 : /* Open() */
212 : /************************************************************************/
213 :
214 153 : GDALDataset *TileDBDataset::Open(GDALOpenInfo *poOpenInfo)
215 :
216 : {
217 : try
218 : {
219 153 : const auto eIdentify = TileDBDataset::Identify(poOpenInfo);
220 153 : if (eIdentify == GDAL_IDENTIFY_FALSE)
221 6 : return nullptr;
222 :
223 147 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB:") &&
224 2 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "TILEDB://"))
225 : {
226 : // subdataset URI so this is a raster
227 2 : return TileDBRasterDataset::Open(poOpenInfo);
228 : }
229 : else
230 : {
231 145 : if ((poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
232 : {
233 26 : return TileDBDataset::OpenMultiDimensional(poOpenInfo);
234 : }
235 :
236 238 : const char *pszConfig = CSLFetchNameValue(
237 119 : poOpenInfo->papszOpenOptions, "TILEDB_CONFIG");
238 238 : tiledb::Context oCtx;
239 :
240 119 : if (pszConfig != nullptr)
241 : {
242 0 : tiledb::Config cfg(pszConfig);
243 0 : oCtx = tiledb::Context(cfg);
244 : }
245 : else
246 : {
247 119 : tiledb::Config cfg;
248 119 : cfg["sm.enable_signal_handlers"] = "false";
249 119 : oCtx = tiledb::Context(cfg);
250 : }
251 : const std::string osPath =
252 238 : TileDBDataset::VSI_to_tiledb_uri(poOpenInfo->pszFilename);
253 :
254 119 : const auto eType = tiledb::Object::object(oCtx, osPath).type();
255 119 : if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
256 : eType == tiledb::Object::Type::Group)
257 : {
258 3 : return OGRTileDBDataset::Open(poOpenInfo, eType);
259 : }
260 116 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
261 : eType == tiledb::Object::Type::Group)
262 : {
263 : // If this is a group which has only a single 2D array and
264 : // no 3D+ arrays, then return this 2D array.
265 : auto poDSUnique = std::unique_ptr<GDALDataset>(
266 2 : TileDBDataset::OpenMultiDimensional(poOpenInfo));
267 1 : if (poDSUnique)
268 : {
269 1 : auto poRootGroup = poDSUnique->GetRootGroup();
270 1 : if (poRootGroup && poRootGroup->GetGroupNames().empty())
271 : {
272 0 : std::shared_ptr<GDALMDArray> poCandidateArray;
273 4 : for (const auto &osName :
274 9 : poRootGroup->GetMDArrayNames())
275 : {
276 4 : auto poArray = poRootGroup->OpenMDArray(osName);
277 4 : if (poArray && poArray->GetDimensionCount() >= 3)
278 : {
279 0 : poCandidateArray.reset();
280 0 : break;
281 : }
282 8 : else if (poArray &&
283 9 : poArray->GetDimensionCount() == 2 &&
284 1 : poArray->GetDimensions()[0]->GetType() ==
285 8 : GDAL_DIM_TYPE_HORIZONTAL_Y &&
286 1 : poArray->GetDimensions()[1]->GetType() ==
287 : GDAL_DIM_TYPE_HORIZONTAL_X)
288 : {
289 1 : if (!poCandidateArray)
290 : {
291 1 : poCandidateArray = std::move(poArray);
292 : }
293 : else
294 : {
295 0 : poCandidateArray.reset();
296 0 : break;
297 : }
298 : }
299 : }
300 1 : if (poCandidateArray)
301 : {
302 1 : return poCandidateArray->AsClassicDataset(1, 0);
303 : }
304 : }
305 : }
306 0 : return nullptr;
307 : }
308 :
309 230 : tiledb::ArraySchema schema(oCtx, osPath);
310 :
311 115 : if (schema.array_type() == TILEDB_SPARSE)
312 35 : return OGRTileDBDataset::Open(poOpenInfo, eType);
313 : else
314 80 : return TileDBRasterDataset::Open(poOpenInfo);
315 : }
316 : }
317 0 : catch (const tiledb::TileDBError &e)
318 : {
319 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
320 0 : return nullptr;
321 : }
322 : }
323 :
324 : /************************************************************************/
325 : /* Create() */
326 : /************************************************************************/
327 :
328 85 : GDALDataset *TileDBDataset::Create(const char *pszFilename, int nXSize,
329 : int nYSize, int nBandsIn, GDALDataType eType,
330 : char **papszOptions)
331 :
332 : {
333 : try
334 : {
335 85 : if (nBandsIn > 0)
336 35 : return TileDBRasterDataset::Create(pszFilename, nXSize, nYSize,
337 35 : nBandsIn, eType, papszOptions);
338 : else
339 50 : return OGRTileDBDataset::Create(pszFilename, papszOptions);
340 : }
341 0 : catch (const tiledb::TileDBError &e)
342 : {
343 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
344 : }
345 :
346 0 : return nullptr;
347 : }
348 :
349 : /************************************************************************/
350 : /* CreateCopy() */
351 : /************************************************************************/
352 :
353 60 : GDALDataset *TileDBDataset::CreateCopy(const char *pszFilename,
354 : GDALDataset *poSrcDS, int bStrict,
355 : char **papszOptions,
356 : GDALProgressFunc pfnProgress,
357 : void *pProgressData)
358 :
359 : {
360 60 : if (poSrcDS->GetRootGroup())
361 : {
362 1 : auto poDrv = GDALDriver::FromHandle(GDALGetDriverByName("TileDB"));
363 1 : if (poDrv)
364 : {
365 1 : return poDrv->DefaultCreateCopy(pszFilename, poSrcDS, bStrict,
366 : papszOptions, pfnProgress,
367 1 : pProgressData);
368 : }
369 : }
370 :
371 : try
372 : {
373 61 : if (poSrcDS->GetRasterCount() > 0 ||
374 2 : poSrcDS->GetMetadata("SUBDATASETS"))
375 : {
376 58 : return TileDBRasterDataset::CreateCopy(pszFilename, poSrcDS,
377 : bStrict, papszOptions,
378 45 : pfnProgress, pProgressData);
379 : }
380 : }
381 26 : catch (const tiledb::TileDBError &e)
382 : {
383 13 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
384 : }
385 :
386 14 : return nullptr;
387 : }
388 :
389 : /************************************************************************/
390 : /* GDALRegister_TILEDB() */
391 : /************************************************************************/
392 :
393 15 : void GDALRegister_TileDB()
394 :
395 : {
396 15 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
397 0 : return;
398 :
399 15 : GDALDriver *poDriver = new GDALDriver();
400 15 : TileDBDriverSetCommonMetadata(poDriver);
401 :
402 15 : poDriver->pfnIdentify = TileDBDataset::Identify;
403 15 : poDriver->pfnOpen = TileDBDataset::Open;
404 15 : poDriver->pfnCreate = TileDBDataset::Create;
405 15 : poDriver->pfnCreateCopy = TileDBDataset::CreateCopy;
406 15 : poDriver->pfnDelete = TileDBDataset::Delete;
407 15 : poDriver->pfnCreateMultiDimensional = TileDBDataset::CreateMultiDimensional;
408 :
409 15 : GetGDALDriverManager()->RegisterDriver(poDriver);
410 : }
|