/src/gdal/apps/gdaltindex_lib.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: MapServer |
4 | | * Purpose: Commandline App to build tile index for raster files. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2001, Frank Warmerdam, DM Solutions Group Inc |
9 | | * Copyright (c) 2007-2023, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "cpl_conv.h" |
16 | | #include "cpl_md5.h" |
17 | | #include "cpl_minixml.h" |
18 | | #include "cpl_string.h" |
19 | | #include "cpl_vsi_virtual.h" |
20 | | #include "gdal_utils.h" |
21 | | #include "gdal_priv.h" |
22 | | #include "gdal_utils_priv.h" |
23 | | #include "ogr_api.h" |
24 | | #include "ograrrowarrayhelper.h" |
25 | | #include "ogrsf_frmts.h" |
26 | | #include "ogr_recordbatch.h" |
27 | | #include "ogr_spatialref.h" |
28 | | #include "commonutils.h" |
29 | | #include "gdalargumentparser.h" |
30 | | |
31 | | #include <ctype.h> |
32 | | |
33 | | #include <algorithm> |
34 | | #include <cmath> |
35 | | #include <limits> |
36 | | #include <set> |
37 | | |
38 | | constexpr const char ARROW_FORMAT_INT32[] = "i"; |
39 | | constexpr const char ARROW_FORMAT_FLOAT32[] = "f"; |
40 | | constexpr const char ARROW_FORMAT_FLOAT64[] = "g"; |
41 | | constexpr const char ARROW_FORMAT_STRING[] = "u"; |
42 | | constexpr const char ARROW_FORMAT_BINARY[] = "z"; |
43 | | constexpr const char ARROW_FORMAT_LIST[] = "+l"; |
44 | | constexpr const char ARROW_FORMAT_STRUCT[] = "+s"; |
45 | | constexpr const char GEOPARQUET_GEOM_COL_NAME[] = "geometry"; |
46 | | |
47 | | constexpr int NUM_ITEMS_PROJ_BBOX = 4; |
48 | | constexpr int NUM_ITEMS_PROJ_SHAPE = 2; |
49 | | constexpr int NUM_ITEMS_PROJ_TRANSFORM = 9; |
50 | | |
51 | | constexpr int ARROW_BUF_VALIDITY = 0; |
52 | | constexpr int ARROW_BUF_DATA = 1; |
53 | | constexpr int ARROW_BUF_BYTES = 2; |
54 | | |
55 | | constexpr int COUNT_STAC_EXTENSIONS = 2; |
56 | | |
57 | | typedef enum |
58 | | { |
59 | | FORMAT_AUTO, |
60 | | FORMAT_WKT, |
61 | | FORMAT_EPSG, |
62 | | FORMAT_PROJ |
63 | | } SrcSRSFormat; |
64 | | |
65 | | /************************************************************************/ |
66 | | /* GDALTileIndexRasterMetadata */ |
67 | | /************************************************************************/ |
68 | | |
69 | | struct GDALTileIndexRasterMetadata |
70 | | { |
71 | | OGRFieldType eType = OFTString; |
72 | | std::string osFieldName{}; |
73 | | std::string osRasterItemName{}; |
74 | | }; |
75 | | |
76 | | /************************************************************************/ |
77 | | /* GDALTileIndexOptions */ |
78 | | /************************************************************************/ |
79 | | |
80 | | struct GDALTileIndexOptions |
81 | | { |
82 | | bool bInvokedFromGdalRasterIndex = false; |
83 | | bool bOverwrite = false; |
84 | | bool bSkipErrors = false; |
85 | | std::string osFormat{}; |
86 | | std::string osIndexLayerName{}; |
87 | | std::string osLocationField = "location"; |
88 | | CPLStringList aosLCO{}; |
89 | | std::string osTargetSRS{}; |
90 | | bool bWriteAbsolutePath = false; |
91 | | bool bSkipDifferentProjection = false; |
92 | | std::string osSrcSRSFieldName{}; |
93 | | SrcSRSFormat eSrcSRSFormat = FORMAT_AUTO; |
94 | | double xres = std::numeric_limits<double>::quiet_NaN(); |
95 | | double yres = std::numeric_limits<double>::quiet_NaN(); |
96 | | double xmin = std::numeric_limits<double>::quiet_NaN(); |
97 | | double ymin = std::numeric_limits<double>::quiet_NaN(); |
98 | | double xmax = std::numeric_limits<double>::quiet_NaN(); |
99 | | double ymax = std::numeric_limits<double>::quiet_NaN(); |
100 | | std::string osBandCount{}; |
101 | | std::string osNodata{}; |
102 | | std::string osColorInterp{}; |
103 | | std::string osDataType{}; |
104 | | bool bMaskBand = false; |
105 | | std::vector<std::string> aosMetadata{}; |
106 | | std::string osGTIFilename{}; |
107 | | bool bRecursive = false; |
108 | | double dfMinPixelSize = std::numeric_limits<double>::quiet_NaN(); |
109 | | double dfMaxPixelSize = std::numeric_limits<double>::quiet_NaN(); |
110 | | std::vector<GDALTileIndexRasterMetadata> aoFetchMD{}; |
111 | | std::set<std::string> oSetFilenameFilters{}; |
112 | | GDALProgressFunc pfnProgress = nullptr; |
113 | | void *pProgressData = nullptr; |
114 | | std::string osProfile{}; // Only "STAC-GeoParquet" handled |
115 | | std::string osBaseURL{}; // Used for "STAC-GeoParquet" |
116 | | std::string osIdMethod{}; // Used for "STAC-GeoParquet" |
117 | | std::string osIdMetadataItem{}; // Used for "STAC-GeoParquet" |
118 | | }; |
119 | | |
120 | | /************************************************************************/ |
121 | | /* ReleaseArray() */ |
122 | | /************************************************************************/ |
123 | | |
124 | | static void ReleaseArray(struct ArrowArray *array) |
125 | 0 | { |
126 | 0 | CPLAssert(array->release != nullptr); |
127 | 0 | if (array->buffers) |
128 | 0 | { |
129 | 0 | for (int i = 0; i < static_cast<int>(array->n_buffers); ++i) |
130 | 0 | VSIFree(const_cast<void *>(array->buffers[i])); |
131 | 0 | CPLFree(array->buffers); |
132 | 0 | } |
133 | 0 | if (array->children) |
134 | 0 | { |
135 | 0 | for (int i = 0; i < static_cast<int>(array->n_children); ++i) |
136 | 0 | { |
137 | 0 | if (array->children[i] && array->children[i]->release) |
138 | 0 | { |
139 | 0 | array->children[i]->release(array->children[i]); |
140 | 0 | CPLFree(array->children[i]); |
141 | 0 | } |
142 | 0 | } |
143 | 0 | CPLFree(array->children); |
144 | 0 | } |
145 | 0 | array->release = nullptr; |
146 | 0 | } |
147 | | |
148 | | /************************************************************************/ |
149 | | /* GDALTileIndexAppOptionsGetParser() */ |
150 | | /************************************************************************/ |
151 | | |
152 | | static std::unique_ptr<GDALArgumentParser> GDALTileIndexAppOptionsGetParser( |
153 | | GDALTileIndexOptions *psOptions, |
154 | | GDALTileIndexOptionsForBinary *psOptionsForBinary) |
155 | 0 | { |
156 | 0 | auto argParser = std::make_unique<GDALArgumentParser>( |
157 | 0 | "gdaltindex", /* bForBinary=*/psOptionsForBinary != nullptr); |
158 | |
|
159 | 0 | argParser->add_description( |
160 | 0 | _("Build a tile index from a list of datasets.")); |
161 | |
|
162 | 0 | argParser->add_epilog( |
163 | 0 | _("For more details, see the full documentation for gdaltindex at\n" |
164 | 0 | "https://gdal.org/programs/gdaltindex.html")); |
165 | | |
166 | | // Hidden as used by gdal raster index |
167 | 0 | argParser->add_argument("--invoked-from-gdal-raster-index") |
168 | 0 | .store_into(psOptions->bInvokedFromGdalRasterIndex) |
169 | 0 | .hidden(); |
170 | | |
171 | | // Hidden as used by gdal raster index |
172 | 0 | argParser->add_argument("-skip_errors") |
173 | 0 | .store_into(psOptions->bSkipErrors) |
174 | 0 | .hidden(); |
175 | | |
176 | | // Hidden as used by gdal raster index |
177 | 0 | argParser->add_argument("-profile") |
178 | 0 | .store_into(psOptions->osProfile) |
179 | 0 | .hidden(); |
180 | | |
181 | | // Hidden as used by gdal raster index |
182 | 0 | argParser->add_argument("--base-url") |
183 | 0 | .store_into(psOptions->osBaseURL) |
184 | 0 | .hidden(); |
185 | | |
186 | | // Hidden as used by gdal raster index |
187 | 0 | argParser->add_argument("--id-method") |
188 | 0 | .store_into(psOptions->osIdMethod) |
189 | 0 | .hidden(); |
190 | | |
191 | | // Hidden as used by gdal raster index |
192 | 0 | argParser->add_argument("--id-metadata-item") |
193 | 0 | .store_into(psOptions->osIdMetadataItem) |
194 | 0 | .hidden(); |
195 | |
|
196 | 0 | argParser->add_argument("-overwrite") |
197 | 0 | .flag() |
198 | 0 | .store_into(psOptions->bOverwrite) |
199 | 0 | .help(_("Overwrite the output tile index file if it already exists.")); |
200 | |
|
201 | 0 | argParser->add_argument("-recursive") |
202 | 0 | .flag() |
203 | 0 | .store_into(psOptions->bRecursive) |
204 | 0 | .help(_("Whether directories specified in <file_or_dir> should be " |
205 | 0 | "explored recursively.")); |
206 | |
|
207 | 0 | argParser->add_argument("-filename_filter") |
208 | 0 | .metavar("<val>") |
209 | 0 | .append() |
210 | 0 | .store_into(psOptions->oSetFilenameFilters) |
211 | 0 | .help(_("Pattern that the filenames contained in directories pointed " |
212 | 0 | "by <file_or_dir> should follow.")); |
213 | |
|
214 | 0 | argParser->add_argument("-min_pixel_size") |
215 | 0 | .metavar("<val>") |
216 | 0 | .store_into(psOptions->dfMinPixelSize) |
217 | 0 | .help(_("Minimum pixel size in term of geospatial extent per pixel " |
218 | 0 | "(resolution) that a raster should have to be selected.")); |
219 | |
|
220 | 0 | argParser->add_argument("-max_pixel_size") |
221 | 0 | .metavar("<val>") |
222 | 0 | .store_into(psOptions->dfMaxPixelSize) |
223 | 0 | .help(_("Maximum pixel size in term of geospatial extent per pixel " |
224 | 0 | "(resolution) that a raster should have to be selected.")); |
225 | |
|
226 | 0 | argParser->add_output_format_argument(psOptions->osFormat); |
227 | |
|
228 | 0 | argParser->add_argument("-tileindex") |
229 | 0 | .metavar("<field_name>") |
230 | 0 | .store_into(psOptions->osLocationField) |
231 | 0 | .help(_("Name of the layer in the tile index file.")); |
232 | |
|
233 | 0 | argParser->add_argument("-write_absolute_path") |
234 | 0 | .flag() |
235 | 0 | .store_into(psOptions->bWriteAbsolutePath) |
236 | 0 | .help(_("Write the absolute path of the raster files in the tile index " |
237 | 0 | "file.")); |
238 | |
|
239 | 0 | argParser->add_argument("-skip_different_projection") |
240 | 0 | .flag() |
241 | 0 | .store_into(psOptions->bSkipDifferentProjection) |
242 | 0 | .help(_( |
243 | 0 | "Only files with the same projection as files already inserted in " |
244 | 0 | "the tile index will be inserted (unless -t_srs is specified).")); |
245 | |
|
246 | 0 | argParser->add_argument("-t_srs") |
247 | 0 | .metavar("<srs_def>") |
248 | 0 | .store_into(psOptions->osTargetSRS) |
249 | 0 | .help(_("Geometries of input files will be transformed to the desired " |
250 | 0 | "target coordinate reference system.")); |
251 | |
|
252 | 0 | argParser->add_argument("-src_srs_name") |
253 | 0 | .metavar("<field_name>") |
254 | 0 | .store_into(psOptions->osSrcSRSFieldName) |
255 | 0 | .help(_("Name of the field in the tile index file where the source SRS " |
256 | 0 | "will be stored.")); |
257 | |
|
258 | 0 | argParser->add_argument("-src_srs_format") |
259 | 0 | .metavar("{AUTO|WKT|EPSG|PROJ}") |
260 | 0 | .choices("AUTO", "WKT", "EPSG", "PROJ") |
261 | 0 | .action( |
262 | 0 | [psOptions](const auto &f) |
263 | 0 | { |
264 | 0 | if (f == "WKT") |
265 | 0 | psOptions->eSrcSRSFormat = FORMAT_WKT; |
266 | 0 | else if (f == "EPSG") |
267 | 0 | psOptions->eSrcSRSFormat = FORMAT_EPSG; |
268 | 0 | else if (f == "PROJ") |
269 | 0 | psOptions->eSrcSRSFormat = FORMAT_PROJ; |
270 | 0 | else |
271 | 0 | psOptions->eSrcSRSFormat = FORMAT_AUTO; |
272 | 0 | }) |
273 | 0 | .help(_("Format of the source SRS to store in the tile index file.")); |
274 | |
|
275 | 0 | argParser->add_argument("-lyr_name") |
276 | 0 | .metavar("<name>") |
277 | 0 | .store_into(psOptions->osIndexLayerName) |
278 | 0 | .help(_("Name of the layer in the tile index file.")); |
279 | |
|
280 | 0 | argParser->add_layer_creation_options_argument(psOptions->aosLCO); |
281 | | |
282 | | // GTI driver options |
283 | |
|
284 | 0 | argParser->add_argument("-gti_filename") |
285 | 0 | .metavar("<filename>") |
286 | 0 | .store_into(psOptions->osGTIFilename) |
287 | 0 | .help(_("Filename of the XML Virtual Tile Index file to generate.")); |
288 | | |
289 | | // NOTE: no store_into |
290 | 0 | argParser->add_argument("-tr") |
291 | 0 | .metavar("<xres> <yres>") |
292 | 0 | .nargs(2) |
293 | 0 | .scan<'g', double>() |
294 | 0 | .help(_("Set target resolution.")); |
295 | | |
296 | | // NOTE: no store_into |
297 | 0 | argParser->add_argument("-te") |
298 | 0 | .metavar("<xmin> <ymin> <xmax> <ymax>") |
299 | 0 | .nargs(4) |
300 | 0 | .scan<'g', double>() |
301 | 0 | .help(_("Set target extent in SRS unit.")); |
302 | |
|
303 | 0 | argParser->add_argument("-ot") |
304 | 0 | .metavar("<datatype>") |
305 | 0 | .store_into(psOptions->osDataType) |
306 | 0 | .help(_("Output data type.")); |
307 | |
|
308 | 0 | argParser->add_argument("-bandcount") |
309 | 0 | .metavar("<val>") |
310 | 0 | .store_into(psOptions->osBandCount) |
311 | 0 | .help(_("Number of bands of the tiles of the tile index.")); |
312 | |
|
313 | 0 | argParser->add_argument("-nodata") |
314 | 0 | .metavar("<val>") |
315 | 0 | .append() |
316 | 0 | .store_into(psOptions->osNodata) |
317 | 0 | .help(_("Nodata value of the tiles of the tile index.")); |
318 | | |
319 | | // Should we use choices here? |
320 | 0 | argParser->add_argument("-colorinterp") |
321 | 0 | .metavar("<val>") |
322 | 0 | .append() |
323 | 0 | .store_into(psOptions->osColorInterp) |
324 | 0 | .help(_("Color interpretation of of the tiles of the tile index: red, " |
325 | 0 | "green, blue, alpha, gray, undefined.")); |
326 | |
|
327 | 0 | argParser->add_argument("-mask") |
328 | 0 | .flag() |
329 | 0 | .store_into(psOptions->bMaskBand) |
330 | 0 | .help(_("Add a mask band to the tiles of the tile index.")); |
331 | |
|
332 | 0 | argParser->add_argument("-mo") |
333 | 0 | .metavar("<name>=<value>") |
334 | 0 | .append() |
335 | 0 | .store_into(psOptions->aosMetadata) |
336 | 0 | .help(_("Write an arbitrary layer metadata item, for formats that " |
337 | 0 | "support layer metadata.")); |
338 | | |
339 | | // NOTE: no store_into |
340 | 0 | argParser->add_argument("-fetch_md") |
341 | 0 | .nargs(3) |
342 | 0 | .metavar("<gdal_md_name> <fld_name> <fld_type>") |
343 | 0 | .append() |
344 | 0 | .help("Fetch a metadata item from the raster tile and write it as a " |
345 | 0 | "field in the tile index."); |
346 | |
|
347 | 0 | if (psOptionsForBinary) |
348 | 0 | { |
349 | 0 | argParser->add_quiet_argument(&psOptionsForBinary->bQuiet); |
350 | |
|
351 | 0 | argParser->add_argument("index_file") |
352 | 0 | .metavar("<index_file>") |
353 | 0 | .store_into(psOptionsForBinary->osDest) |
354 | 0 | .help(_("The name of the output file to create/append to.")); |
355 | |
|
356 | 0 | argParser->add_argument("file_or_dir") |
357 | 0 | .metavar("<file_or_dir>") |
358 | 0 | .nargs(argparse::nargs_pattern::at_least_one) |
359 | 0 | .action([psOptionsForBinary](const std::string &s) |
360 | 0 | { psOptionsForBinary->aosSrcFiles.AddString(s.c_str()); }) |
361 | 0 | .help(_( |
362 | 0 | "The input GDAL raster files or directory, can be multiple " |
363 | 0 | "locations separated by spaces. Wildcards may also be used.")); |
364 | 0 | } |
365 | |
|
366 | 0 | return argParser; |
367 | 0 | } |
368 | | |
369 | | /************************************************************************/ |
370 | | /* GDALTileIndexAppGetParserUsage() */ |
371 | | /************************************************************************/ |
372 | | |
373 | | std::string GDALTileIndexAppGetParserUsage() |
374 | 0 | { |
375 | 0 | try |
376 | 0 | { |
377 | 0 | GDALTileIndexOptions sOptions; |
378 | 0 | GDALTileIndexOptionsForBinary sOptionsForBinary; |
379 | 0 | auto argParser = |
380 | 0 | GDALTileIndexAppOptionsGetParser(&sOptions, &sOptionsForBinary); |
381 | 0 | return argParser->usage(); |
382 | 0 | } |
383 | 0 | catch (const std::exception &err) |
384 | 0 | { |
385 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", |
386 | 0 | err.what()); |
387 | 0 | return std::string(); |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | | /************************************************************************/ |
392 | | /* GDALTileIndexTileIterator */ |
393 | | /************************************************************************/ |
394 | | |
395 | | struct GDALTileIndexTileIterator |
396 | | { |
397 | | const GDALTileIndexOptions *psOptions = nullptr; |
398 | | int nSrcCount = 0; |
399 | | const char *const *papszSrcDSNames = nullptr; |
400 | | std::string osCurDir{}; |
401 | | int iCurSrc = 0; |
402 | | VSIDIR *psDir = nullptr; |
403 | | |
404 | | CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexTileIterator) |
405 | | |
406 | | GDALTileIndexTileIterator(const GDALTileIndexOptions *psOptionsIn, |
407 | | int nSrcCountIn, |
408 | | const char *const *papszSrcDSNamesIn) |
409 | 0 | : psOptions(psOptionsIn), nSrcCount(nSrcCountIn), |
410 | 0 | papszSrcDSNames(papszSrcDSNamesIn) |
411 | 0 | { |
412 | 0 | } |
413 | | |
414 | | void reset() |
415 | 0 | { |
416 | 0 | if (psDir) |
417 | 0 | VSICloseDir(psDir); |
418 | 0 | psDir = nullptr; |
419 | 0 | iCurSrc = 0; |
420 | 0 | } |
421 | | |
422 | | std::string next() |
423 | 0 | { |
424 | 0 | while (true) |
425 | 0 | { |
426 | 0 | if (!psDir) |
427 | 0 | { |
428 | 0 | if (iCurSrc == nSrcCount) |
429 | 0 | { |
430 | 0 | break; |
431 | 0 | } |
432 | | |
433 | 0 | VSIStatBufL sStatBuf; |
434 | 0 | const char *pszCurName = papszSrcDSNames[iCurSrc++]; |
435 | 0 | if (VSIStatL(pszCurName, &sStatBuf) == 0 && |
436 | 0 | VSI_ISDIR(sStatBuf.st_mode)) |
437 | 0 | { |
438 | 0 | auto poSrcDS = std::unique_ptr<GDALDataset>( |
439 | 0 | GDALDataset::Open(pszCurName, GDAL_OF_RASTER, nullptr, |
440 | 0 | nullptr, nullptr)); |
441 | 0 | if (poSrcDS) |
442 | 0 | return pszCurName; |
443 | | |
444 | 0 | osCurDir = pszCurName; |
445 | 0 | psDir = VSIOpenDir( |
446 | 0 | osCurDir.c_str(), |
447 | 0 | /*nDepth=*/psOptions->bRecursive ? -1 : 0, nullptr); |
448 | 0 | if (!psDir) |
449 | 0 | { |
450 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
451 | 0 | "Cannot open directory %s", osCurDir.c_str()); |
452 | 0 | return std::string(); |
453 | 0 | } |
454 | 0 | } |
455 | 0 | else |
456 | 0 | { |
457 | 0 | return pszCurName; |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | 0 | auto psEntry = VSIGetNextDirEntry(psDir); |
462 | 0 | if (!psEntry) |
463 | 0 | { |
464 | 0 | VSICloseDir(psDir); |
465 | 0 | psDir = nullptr; |
466 | 0 | continue; |
467 | 0 | } |
468 | | |
469 | 0 | if (!psOptions->oSetFilenameFilters.empty()) |
470 | 0 | { |
471 | 0 | bool bMatchFound = false; |
472 | 0 | const std::string osFilenameOnly = |
473 | 0 | CPLGetFilename(psEntry->pszName); |
474 | 0 | for (const auto &osFilter : psOptions->oSetFilenameFilters) |
475 | 0 | { |
476 | 0 | if (GDALPatternMatch(osFilenameOnly.c_str(), |
477 | 0 | osFilter.c_str())) |
478 | 0 | { |
479 | 0 | bMatchFound = true; |
480 | 0 | break; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | if (!bMatchFound) |
484 | 0 | continue; |
485 | 0 | } |
486 | | |
487 | 0 | std::string osFilename = CPLFormFilenameSafe( |
488 | 0 | osCurDir.c_str(), psEntry->pszName, nullptr); |
489 | 0 | if (VSI_ISDIR(psEntry->nMode)) |
490 | 0 | { |
491 | 0 | auto poSrcDS = std::unique_ptr<GDALDataset>( |
492 | 0 | GDALDataset::Open(osFilename.c_str(), GDAL_OF_RASTER, |
493 | 0 | nullptr, nullptr, nullptr)); |
494 | 0 | if (poSrcDS) |
495 | 0 | { |
496 | 0 | return osFilename; |
497 | 0 | } |
498 | 0 | continue; |
499 | 0 | } |
500 | | |
501 | 0 | return osFilename; |
502 | 0 | } |
503 | 0 | return std::string(); |
504 | 0 | } |
505 | | }; |
506 | | |
507 | | /************************************************************************/ |
508 | | /* GDALTileIndex() */ |
509 | | /************************************************************************/ |
510 | | |
511 | | /* clang-format off */ |
512 | | /** |
513 | | * Build a tile index from a list of datasets. |
514 | | * |
515 | | * This is the equivalent of the |
516 | | * <a href="/programs/gdaltindex.html">gdaltindex</a> utility. |
517 | | * |
518 | | * GDALTileIndexOptions* must be allocated and freed with |
519 | | * GDALTileIndexOptionsNew() and GDALTileIndexOptionsFree() respectively. |
520 | | * |
521 | | * @param pszDest the destination dataset path. |
522 | | * @param nSrcCount the number of input datasets. |
523 | | * @param papszSrcDSNames the list of input dataset names |
524 | | * @param psOptionsIn the options struct returned by GDALTileIndexOptionsNew() or |
525 | | * NULL. |
526 | | * @param pbUsageError pointer to a integer output variable to store if any |
527 | | * usage error has occurred. |
528 | | * @return the output dataset (new dataset that must be closed using |
529 | | * GDALClose()) or NULL in case of error. |
530 | | * |
531 | | * @since GDAL3.9 |
532 | | */ |
533 | | /* clang-format on */ |
534 | | |
535 | | GDALDatasetH GDALTileIndex(const char *pszDest, int nSrcCount, |
536 | | const char *const *papszSrcDSNames, |
537 | | const GDALTileIndexOptions *psOptionsIn, |
538 | | int *pbUsageError) |
539 | 0 | { |
540 | 0 | return GDALTileIndexInternal(pszDest, nullptr, nullptr, nSrcCount, |
541 | 0 | papszSrcDSNames, psOptionsIn, pbUsageError); |
542 | 0 | } |
543 | | |
544 | | GDALDatasetH GDALTileIndexInternal(const char *pszDest, |
545 | | GDALDatasetH hTileIndexDS, OGRLayerH hLayer, |
546 | | int nSrcCount, |
547 | | const char *const *papszSrcDSNames, |
548 | | const GDALTileIndexOptions *psOptionsIn, |
549 | | int *pbUsageError) |
550 | 0 | { |
551 | 0 | if (nSrcCount == 0) |
552 | 0 | { |
553 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "No input dataset specified."); |
554 | |
|
555 | 0 | if (pbUsageError) |
556 | 0 | *pbUsageError = TRUE; |
557 | 0 | return nullptr; |
558 | 0 | } |
559 | | |
560 | 0 | auto psOptions = psOptionsIn |
561 | 0 | ? std::make_unique<GDALTileIndexOptions>(*psOptionsIn) |
562 | 0 | : std::make_unique<GDALTileIndexOptions>(); |
563 | |
|
564 | 0 | GDALTileIndexTileIterator oGDALTileIndexTileIterator( |
565 | 0 | psOptions.get(), nSrcCount, papszSrcDSNames); |
566 | | |
567 | | /* -------------------------------------------------------------------- */ |
568 | | /* Create and validate target SRS if given. */ |
569 | | /* -------------------------------------------------------------------- */ |
570 | 0 | OGRSpatialReference oTargetSRS; |
571 | 0 | if (!psOptions->osTargetSRS.empty()) |
572 | 0 | { |
573 | 0 | if (psOptions->bSkipDifferentProjection) |
574 | 0 | { |
575 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
576 | 0 | "-skip_different_projections does not apply " |
577 | 0 | "when -t_srs is requested."); |
578 | 0 | } |
579 | 0 | oTargetSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
580 | | // coverity[tainted_data] |
581 | 0 | oTargetSRS.SetFromUserInput(psOptions->osTargetSRS.c_str()); |
582 | 0 | } |
583 | | |
584 | | /* -------------------------------------------------------------------- */ |
585 | | /* Open or create the target datasource */ |
586 | | /* -------------------------------------------------------------------- */ |
587 | |
|
588 | 0 | std::unique_ptr<GDALDataset> poTileIndexDSUnique; |
589 | 0 | GDALDataset *poTileIndexDS = GDALDataset::FromHandle(hTileIndexDS); |
590 | 0 | OGRLayer *poLayer = OGRLayer::FromHandle(hLayer); |
591 | 0 | bool bExistingLayer = false; |
592 | 0 | std::string osFormat; |
593 | |
|
594 | 0 | if (!hTileIndexDS) |
595 | 0 | { |
596 | 0 | if (psOptions->bOverwrite) |
597 | 0 | { |
598 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
599 | 0 | auto hDriver = GDALIdentifyDriver(pszDest, nullptr); |
600 | 0 | if (hDriver) |
601 | 0 | GDALDeleteDataset(hDriver, pszDest); |
602 | 0 | else |
603 | 0 | VSIUnlink(pszDest); |
604 | 0 | CPLPopErrorHandler(); |
605 | 0 | } |
606 | |
|
607 | 0 | poTileIndexDSUnique.reset( |
608 | 0 | GDALDataset::Open(pszDest, GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, |
609 | 0 | nullptr, nullptr)); |
610 | |
|
611 | 0 | if (poTileIndexDSUnique != nullptr) |
612 | 0 | { |
613 | 0 | auto poDriver = poTileIndexDSUnique->GetDriver(); |
614 | 0 | if (poDriver) |
615 | 0 | osFormat = poDriver->GetDescription(); |
616 | |
|
617 | 0 | if (poTileIndexDSUnique->GetLayerCount() == 1) |
618 | 0 | { |
619 | 0 | poLayer = poTileIndexDSUnique->GetLayer(0); |
620 | 0 | } |
621 | 0 | else |
622 | 0 | { |
623 | 0 | if (psOptions->osIndexLayerName.empty()) |
624 | 0 | { |
625 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
626 | 0 | "Multiple layers detected: -lyr_name must be " |
627 | 0 | "specified."); |
628 | 0 | if (pbUsageError) |
629 | 0 | *pbUsageError = true; |
630 | 0 | return nullptr; |
631 | 0 | } |
632 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
633 | 0 | poLayer = poTileIndexDSUnique->GetLayerByName( |
634 | 0 | psOptions->osIndexLayerName.c_str()); |
635 | 0 | CPLPopErrorHandler(); |
636 | 0 | } |
637 | 0 | } |
638 | 0 | else |
639 | 0 | { |
640 | 0 | if (psOptions->osFormat.empty()) |
641 | 0 | { |
642 | 0 | const auto aoDrivers = |
643 | 0 | GetOutputDriversFor(pszDest, GDAL_OF_VECTOR); |
644 | 0 | if (aoDrivers.empty()) |
645 | 0 | { |
646 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
647 | 0 | "Cannot guess driver for %s", pszDest); |
648 | 0 | return nullptr; |
649 | 0 | } |
650 | 0 | else |
651 | 0 | { |
652 | 0 | if (aoDrivers.size() > 1) |
653 | 0 | { |
654 | 0 | CPLError( |
655 | 0 | CE_Warning, CPLE_AppDefined, |
656 | 0 | "Several drivers matching %s extension. Using %s", |
657 | 0 | CPLGetExtensionSafe(pszDest).c_str(), |
658 | 0 | aoDrivers[0].c_str()); |
659 | 0 | } |
660 | 0 | osFormat = aoDrivers[0]; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | else |
664 | 0 | { |
665 | 0 | osFormat = psOptions->osFormat; |
666 | 0 | } |
667 | | |
668 | 0 | auto poDriver = |
669 | 0 | GetGDALDriverManager()->GetDriverByName(osFormat.c_str()); |
670 | 0 | if (poDriver == nullptr) |
671 | 0 | { |
672 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
673 | 0 | "%s driver not available.", osFormat.c_str()); |
674 | 0 | return nullptr; |
675 | 0 | } |
676 | | |
677 | 0 | poTileIndexDSUnique.reset( |
678 | 0 | poDriver->Create(pszDest, 0, 0, 0, GDT_Unknown, nullptr)); |
679 | 0 | if (!poTileIndexDSUnique) |
680 | 0 | return nullptr; |
681 | 0 | } |
682 | | |
683 | 0 | poTileIndexDS = poTileIndexDSUnique.get(); |
684 | 0 | } |
685 | | |
686 | 0 | const bool bIsSTACGeoParquet = |
687 | 0 | EQUAL(psOptions->osProfile.c_str(), "STAC-GeoParquet"); |
688 | |
|
689 | 0 | auto poOutDrv = poTileIndexDS->GetDriver(); |
690 | 0 | if (osFormat.empty() && poOutDrv) |
691 | 0 | osFormat = poOutDrv->GetDescription(); |
692 | |
|
693 | 0 | const char *pszVal = |
694 | 0 | poOutDrv ? poOutDrv->GetMetadataItem(GDAL_DMD_MAX_STRING_LENGTH) |
695 | 0 | : nullptr; |
696 | 0 | const int nMaxFieldSize = pszVal ? atoi(pszVal) : 0; |
697 | |
|
698 | 0 | const bool bFailOnErrors = |
699 | 0 | psOptions->bInvokedFromGdalRasterIndex && !psOptions->bSkipErrors; |
700 | 0 | bool bSkipFirstTile = false; |
701 | | |
702 | | // Configurable mostly/only for autotest purposes. |
703 | 0 | const int nMaxBatchSize = std::max( |
704 | 0 | 1, atoi(CPLGetConfigOption("GDAL_RASTER_INDEX_BATCH_SIZE", "65536"))); |
705 | |
|
706 | 0 | std::vector<ArrowSchema> topSchemas; |
707 | 0 | std::vector<ArrowSchema *> topSchemasPointers; |
708 | 0 | std::vector<std::unique_ptr<ArrowSchema>> auxSchemas; |
709 | 0 | std::vector<ArrowSchema *> stacExtensionsSchemaChildren, |
710 | 0 | linksSchemaChildren, linksItemSchemaChildren, assetsSchemaChildren, |
711 | 0 | imageAssetSchemaChildren, imageAssetRolesSchemaChildren, |
712 | 0 | bandsSchemaChildren, bandsItemSchemaChildren, projBboxSchemaChildren, |
713 | 0 | projShapeSchemaChildren, projTransformSchemaChildren; |
714 | 0 | ArrowSchema topSchema{}; |
715 | 0 | const auto noop_release = [](struct ArrowSchema *) {}; |
716 | 0 | const auto AddTopSchema = [&topSchemas, &noop_release]() -> ArrowSchema & |
717 | 0 | { |
718 | 0 | topSchemas.push_back(ArrowSchema{}); |
719 | 0 | topSchemas.back().release = noop_release; |
720 | 0 | return topSchemas.back(); |
721 | 0 | }; |
722 | |
|
723 | 0 | if (poLayer) |
724 | 0 | { |
725 | 0 | bExistingLayer = true; |
726 | 0 | } |
727 | 0 | else |
728 | 0 | { |
729 | 0 | std::string osLayerName; |
730 | 0 | if (psOptions->osIndexLayerName.empty()) |
731 | 0 | { |
732 | 0 | VSIStatBuf sStat; |
733 | 0 | if (EQUAL(osFormat.c_str(), "ESRI Shapefile") || |
734 | 0 | VSIStat(pszDest, &sStat) == 0) |
735 | 0 | { |
736 | 0 | osLayerName = CPLGetBasenameSafe(pszDest); |
737 | 0 | } |
738 | 0 | else |
739 | 0 | { |
740 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
741 | 0 | "-lyr_name must be specified."); |
742 | 0 | if (pbUsageError) |
743 | 0 | *pbUsageError = true; |
744 | 0 | return nullptr; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | else |
748 | 0 | { |
749 | 0 | if (psOptions->bOverwrite) |
750 | 0 | { |
751 | 0 | for (int i = 0; i < poTileIndexDS->GetLayerCount(); ++i) |
752 | 0 | { |
753 | 0 | auto poExistingLayer = poTileIndexDS->GetLayer(i); |
754 | 0 | if (poExistingLayer && poExistingLayer->GetName() == |
755 | 0 | psOptions->osIndexLayerName) |
756 | 0 | { |
757 | 0 | if (poTileIndexDS->DeleteLayer(i) != OGRERR_NONE) |
758 | 0 | return nullptr; |
759 | 0 | break; |
760 | 0 | } |
761 | 0 | } |
762 | 0 | } |
763 | | |
764 | 0 | osLayerName = psOptions->osIndexLayerName; |
765 | 0 | } |
766 | | |
767 | | /* get spatial reference for output file from target SRS (if set) */ |
768 | | /* or from first input file */ |
769 | 0 | OGRSpatialReference oSRS; |
770 | 0 | if (!oTargetSRS.IsEmpty()) |
771 | 0 | { |
772 | 0 | oSRS = oTargetSRS; |
773 | 0 | } |
774 | 0 | else |
775 | 0 | { |
776 | 0 | std::string osFilename = oGDALTileIndexTileIterator.next(); |
777 | 0 | if (osFilename.empty()) |
778 | 0 | { |
779 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find any tile"); |
780 | 0 | return nullptr; |
781 | 0 | } |
782 | 0 | oGDALTileIndexTileIterator.reset(); |
783 | 0 | std::unique_ptr<CPLTurnFailureIntoWarningBackuper> |
784 | 0 | poFailureIntoWarning; |
785 | 0 | if (!bFailOnErrors) |
786 | 0 | poFailureIntoWarning = |
787 | 0 | std::make_unique<CPLTurnFailureIntoWarningBackuper>(); |
788 | 0 | CPL_IGNORE_RET_VAL(poFailureIntoWarning); |
789 | 0 | auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
790 | 0 | osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, |
791 | 0 | nullptr, nullptr, nullptr)); |
792 | 0 | if (!poSrcDS) |
793 | 0 | { |
794 | 0 | CPLError(bFailOnErrors ? CE_Failure : CE_Warning, |
795 | 0 | CPLE_AppDefined, "Unable to open %s%s.", |
796 | 0 | osFilename.c_str(), bFailOnErrors ? "" : ", skipping"); |
797 | 0 | if (bFailOnErrors) |
798 | 0 | return nullptr; |
799 | 0 | bSkipFirstTile = true; |
800 | 0 | } |
801 | 0 | else |
802 | 0 | { |
803 | 0 | auto poSrcSRS = poSrcDS->GetSpatialRef(); |
804 | 0 | if (poSrcSRS) |
805 | 0 | oSRS = *poSrcSRS; |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | 0 | if (bIsSTACGeoParquet) |
810 | 0 | { |
811 | 0 | psOptions->aosLCO.SetNameValue("ROW_GROUP_SIZE", |
812 | 0 | CPLSPrintf("%d", nMaxBatchSize)); |
813 | 0 | psOptions->aosLCO.SetNameValue("GEOMETRY_ENCODING", "WKB"); |
814 | 0 | psOptions->aosLCO.SetNameValue("GEOMETRY_NAME", |
815 | 0 | GEOPARQUET_GEOM_COL_NAME); |
816 | 0 | psOptions->aosLCO.SetNameValue("FID", ""); |
817 | 0 | psOptions->aosLCO.SetNameValue("WRITE_COVERING_BBOX", "YES"); |
818 | 0 | psOptions->aosLCO.SetNameValue("COVERING_BBOX_NAME", "bbox"); |
819 | 0 | if (CPLTestBool( |
820 | 0 | psOptions->aosLCO.FetchNameValueDef("SORT_BY_BBOX", "NO"))) |
821 | 0 | { |
822 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
823 | 0 | "SORT_BY_BBOX=YES is not compatible with " |
824 | 0 | "STAC-GeoParquet profile"); |
825 | 0 | return nullptr; |
826 | 0 | } |
827 | 0 | } |
828 | | |
829 | 0 | poLayer = poTileIndexDS->CreateLayer( |
830 | 0 | osLayerName.c_str(), oSRS.IsEmpty() ? nullptr : &oSRS, wkbPolygon, |
831 | 0 | psOptions->aosLCO.List()); |
832 | 0 | if (!poLayer) |
833 | 0 | return nullptr; |
834 | | |
835 | 0 | if (bIsSTACGeoParquet) |
836 | 0 | { |
837 | 0 | const auto AddAuxSchema = [&auxSchemas, &noop_release]() |
838 | 0 | { |
839 | 0 | auto newSchema = std::make_unique<ArrowSchema>(ArrowSchema{}); |
840 | 0 | newSchema->release = noop_release; |
841 | 0 | auxSchemas.push_back(std::move(newSchema)); |
842 | 0 | return auxSchemas.back().get(); |
843 | 0 | }; |
844 | | |
845 | | // "id" field |
846 | 0 | { |
847 | 0 | auto &schema = AddTopSchema(); |
848 | 0 | schema.format = ARROW_FORMAT_STRING; |
849 | 0 | schema.name = "id"; |
850 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
851 | 0 | return nullptr; |
852 | 0 | } |
853 | | // "stac_extensions" field |
854 | 0 | { |
855 | 0 | auto &schema = AddTopSchema(); |
856 | |
|
857 | 0 | auto &sub_schema = *AddAuxSchema(); |
858 | 0 | stacExtensionsSchemaChildren.push_back(&sub_schema); |
859 | |
|
860 | 0 | schema.format = ARROW_FORMAT_LIST; |
861 | 0 | schema.name = "stac_extensions"; |
862 | 0 | schema.n_children = 1; |
863 | 0 | schema.children = stacExtensionsSchemaChildren.data(); |
864 | 0 | sub_schema.format = ARROW_FORMAT_STRING; |
865 | 0 | sub_schema.name = "item"; |
866 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
867 | 0 | return nullptr; |
868 | 0 | } |
869 | | // "links" field |
870 | 0 | { |
871 | 0 | auto &links = AddTopSchema(); |
872 | |
|
873 | 0 | auto &item = *AddAuxSchema(); |
874 | 0 | linksSchemaChildren.push_back(&item); |
875 | |
|
876 | 0 | auto &href = *AddAuxSchema(); |
877 | 0 | linksItemSchemaChildren.push_back(&href); |
878 | |
|
879 | 0 | auto &rel = *AddAuxSchema(); |
880 | 0 | linksItemSchemaChildren.push_back(&rel); |
881 | |
|
882 | 0 | auto &type = *AddAuxSchema(); |
883 | 0 | linksItemSchemaChildren.push_back(&type); |
884 | |
|
885 | 0 | auto &title = *AddAuxSchema(); |
886 | 0 | linksItemSchemaChildren.push_back(&title); |
887 | |
|
888 | 0 | links.format = ARROW_FORMAT_LIST; |
889 | 0 | links.name = "links"; |
890 | 0 | links.n_children = linksSchemaChildren.size(); |
891 | 0 | links.children = linksSchemaChildren.data(); |
892 | |
|
893 | 0 | item.format = ARROW_FORMAT_STRUCT; |
894 | 0 | item.name = "item"; |
895 | 0 | item.n_children = linksItemSchemaChildren.size(); |
896 | 0 | item.children = linksItemSchemaChildren.data(); |
897 | |
|
898 | 0 | href.format = ARROW_FORMAT_STRING; |
899 | 0 | href.name = "href"; |
900 | |
|
901 | 0 | rel.format = ARROW_FORMAT_STRING; |
902 | 0 | rel.name = "rel"; |
903 | |
|
904 | 0 | type.format = ARROW_FORMAT_STRING; |
905 | 0 | type.name = "type"; |
906 | 0 | type.flags = ARROW_FLAG_NULLABLE; |
907 | |
|
908 | 0 | title.format = ARROW_FORMAT_STRING; |
909 | 0 | title.name = "title"; |
910 | 0 | title.flags = ARROW_FLAG_NULLABLE; |
911 | |
|
912 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&links)) |
913 | 0 | return nullptr; |
914 | 0 | } |
915 | | |
916 | | // "assets" field |
917 | 0 | { |
918 | 0 | auto &assets = AddTopSchema(); |
919 | |
|
920 | 0 | auto &image = *AddAuxSchema(); |
921 | 0 | assetsSchemaChildren.push_back(&image); |
922 | |
|
923 | 0 | auto &href = *AddAuxSchema(); |
924 | 0 | imageAssetSchemaChildren.push_back(&href); |
925 | |
|
926 | 0 | auto &roles = *AddAuxSchema(); |
927 | 0 | imageAssetSchemaChildren.push_back(&roles); |
928 | |
|
929 | 0 | auto &title = *AddAuxSchema(); |
930 | 0 | imageAssetSchemaChildren.push_back(&title); |
931 | |
|
932 | 0 | auto &type = *AddAuxSchema(); |
933 | 0 | imageAssetSchemaChildren.push_back(&type); |
934 | |
|
935 | 0 | auto &roles_item = *AddAuxSchema(); |
936 | 0 | imageAssetRolesSchemaChildren.push_back(&roles_item); |
937 | |
|
938 | 0 | assets.format = ARROW_FORMAT_STRUCT; |
939 | 0 | assets.name = "assets"; |
940 | 0 | assets.n_children = assetsSchemaChildren.size(); |
941 | 0 | assets.children = assetsSchemaChildren.data(); |
942 | |
|
943 | 0 | image.format = ARROW_FORMAT_STRUCT; |
944 | 0 | image.name = "image"; |
945 | 0 | image.n_children = imageAssetSchemaChildren.size(); |
946 | 0 | image.children = imageAssetSchemaChildren.data(); |
947 | |
|
948 | 0 | href.format = ARROW_FORMAT_STRING; |
949 | 0 | href.name = "href"; |
950 | |
|
951 | 0 | roles.format = ARROW_FORMAT_LIST; |
952 | 0 | roles.name = "roles"; |
953 | 0 | roles.flags = ARROW_FLAG_NULLABLE; |
954 | 0 | roles.n_children = imageAssetRolesSchemaChildren.size(); |
955 | 0 | roles.children = imageAssetRolesSchemaChildren.data(); |
956 | |
|
957 | 0 | roles_item.format = ARROW_FORMAT_STRING; |
958 | 0 | roles_item.name = "item"; |
959 | 0 | roles_item.flags = ARROW_FLAG_NULLABLE; |
960 | |
|
961 | 0 | title.format = ARROW_FORMAT_STRING; |
962 | 0 | title.name = "title"; |
963 | 0 | title.flags = ARROW_FLAG_NULLABLE; |
964 | |
|
965 | 0 | type.format = ARROW_FORMAT_STRING; |
966 | 0 | type.name = "type"; |
967 | 0 | type.flags = ARROW_FLAG_NULLABLE; |
968 | |
|
969 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&assets)) |
970 | 0 | return nullptr; |
971 | 0 | } |
972 | | |
973 | | // "bands" field |
974 | 0 | { |
975 | 0 | auto &bands = AddTopSchema(); |
976 | |
|
977 | 0 | auto &bandsItem = *AddAuxSchema(); |
978 | 0 | bandsSchemaChildren.push_back(&bandsItem); |
979 | |
|
980 | 0 | bands.format = ARROW_FORMAT_LIST; |
981 | 0 | bands.name = "bands"; |
982 | 0 | bands.n_children = bandsSchemaChildren.size(); |
983 | 0 | bands.children = bandsSchemaChildren.data(); |
984 | |
|
985 | 0 | auto &name = *AddAuxSchema(); |
986 | 0 | bandsItemSchemaChildren.push_back(&name); |
987 | |
|
988 | 0 | auto &commonName = *AddAuxSchema(); |
989 | 0 | bandsItemSchemaChildren.push_back(&commonName); |
990 | |
|
991 | 0 | auto ¢erWavelength = *AddAuxSchema(); |
992 | 0 | bandsItemSchemaChildren.push_back(¢erWavelength); |
993 | |
|
994 | 0 | auto &fullWidthHalfMax = *AddAuxSchema(); |
995 | 0 | bandsItemSchemaChildren.push_back(&fullWidthHalfMax); |
996 | |
|
997 | 0 | auto &nodata = *AddAuxSchema(); |
998 | 0 | bandsItemSchemaChildren.push_back(&nodata); |
999 | |
|
1000 | 0 | auto &data_type = *AddAuxSchema(); |
1001 | 0 | bandsItemSchemaChildren.push_back(&data_type); |
1002 | |
|
1003 | 0 | auto &unit = *AddAuxSchema(); |
1004 | 0 | bandsItemSchemaChildren.push_back(&unit); |
1005 | |
|
1006 | 0 | bandsItem.format = ARROW_FORMAT_STRUCT; |
1007 | 0 | bandsItem.name = "item"; |
1008 | 0 | bandsItem.n_children = bandsItemSchemaChildren.size(); |
1009 | 0 | bandsItem.children = bandsItemSchemaChildren.data(); |
1010 | |
|
1011 | 0 | name.format = ARROW_FORMAT_STRING; |
1012 | 0 | name.name = "name"; |
1013 | |
|
1014 | 0 | commonName.format = ARROW_FORMAT_STRING; |
1015 | 0 | commonName.name = "eo:common_name"; |
1016 | 0 | commonName.flags = ARROW_FLAG_NULLABLE; |
1017 | |
|
1018 | 0 | centerWavelength.format = ARROW_FORMAT_FLOAT32; |
1019 | 0 | centerWavelength.name = "eo:center_wavelength"; |
1020 | 0 | centerWavelength.flags = ARROW_FLAG_NULLABLE; |
1021 | |
|
1022 | 0 | fullWidthHalfMax.format = ARROW_FORMAT_FLOAT32; |
1023 | 0 | fullWidthHalfMax.name = "eo:full_width_half_max"; |
1024 | 0 | fullWidthHalfMax.flags = ARROW_FLAG_NULLABLE; |
1025 | |
|
1026 | 0 | nodata.format = ARROW_FORMAT_STRING; |
1027 | 0 | nodata.name = "nodata"; |
1028 | 0 | nodata.flags = ARROW_FLAG_NULLABLE; |
1029 | |
|
1030 | 0 | data_type.format = ARROW_FORMAT_STRING; |
1031 | 0 | data_type.name = "data_type"; |
1032 | |
|
1033 | 0 | unit.format = ARROW_FORMAT_STRING; |
1034 | 0 | unit.name = "unit"; |
1035 | 0 | unit.flags = ARROW_FLAG_NULLABLE; |
1036 | |
|
1037 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&bands)) |
1038 | 0 | return nullptr; |
1039 | 0 | } |
1040 | | |
1041 | | // "proj:code" field |
1042 | 0 | { |
1043 | 0 | auto &schema = AddTopSchema(); |
1044 | 0 | schema.format = ARROW_FORMAT_STRING; |
1045 | 0 | schema.name = "proj:code"; |
1046 | 0 | schema.flags = ARROW_FLAG_NULLABLE; |
1047 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1048 | 0 | return nullptr; |
1049 | 0 | } |
1050 | | // "proj:wkt2" field |
1051 | 0 | { |
1052 | 0 | auto &schema = AddTopSchema(); |
1053 | 0 | schema.format = ARROW_FORMAT_STRING; |
1054 | 0 | schema.name = "proj:wkt2"; |
1055 | 0 | schema.flags = ARROW_FLAG_NULLABLE; |
1056 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1057 | 0 | return nullptr; |
1058 | 0 | } |
1059 | | // "proj:projjson" field |
1060 | 0 | { |
1061 | 0 | auto &schema = AddTopSchema(); |
1062 | 0 | schema.format = ARROW_FORMAT_STRING; |
1063 | 0 | schema.name = "proj:projjson"; |
1064 | | // clang-format off |
1065 | 0 | static const char jsonMetadata[] = { |
1066 | | // Number of key/value pairs (uint32) |
1067 | 0 | 1, 0, 0, 0, |
1068 | | // Length of key (uint32) |
1069 | 0 | 20, 0, 0, 0, |
1070 | 0 | 'A', 'R', 'R', 'O', 'W', ':', |
1071 | 0 | 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', ':', |
1072 | 0 | 'n', 'a', 'm', 'e', |
1073 | | // Length of value (uint32) |
1074 | 0 | 10, 0, 0, 0, |
1075 | 0 | 'a', 'r', 'r', 'o', 'w', '.', 'j', 's', 'o', 'n', |
1076 | 0 | }; |
1077 | | // clang-format on |
1078 | 0 | schema.metadata = jsonMetadata; |
1079 | 0 | schema.flags = ARROW_FLAG_NULLABLE; |
1080 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1081 | 0 | return nullptr; |
1082 | 0 | } |
1083 | | // "proj:bbox" field |
1084 | 0 | { |
1085 | 0 | auto &schema = AddTopSchema(); |
1086 | 0 | static const char FORMAT_PROJ_BBOX[] = { |
1087 | 0 | '+', 'w', ':', '0' + NUM_ITEMS_PROJ_BBOX, 0}; |
1088 | 0 | schema.format = FORMAT_PROJ_BBOX; |
1089 | 0 | schema.name = "proj:bbox"; |
1090 | |
|
1091 | 0 | auto &sub_schema = *AddAuxSchema(); |
1092 | 0 | projBboxSchemaChildren.push_back(&sub_schema); |
1093 | |
|
1094 | 0 | schema.n_children = projBboxSchemaChildren.size(); |
1095 | 0 | schema.children = projBboxSchemaChildren.data(); |
1096 | 0 | sub_schema.format = ARROW_FORMAT_FLOAT64; |
1097 | 0 | sub_schema.name = "item"; |
1098 | |
|
1099 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1100 | 0 | return nullptr; |
1101 | 0 | } |
1102 | | // "proj:shape" field |
1103 | 0 | { |
1104 | 0 | auto &schema = AddTopSchema(); |
1105 | 0 | static const char FORMAT_PROJ_SHAPE[] = { |
1106 | 0 | '+', 'w', ':', '0' + NUM_ITEMS_PROJ_SHAPE, 0}; |
1107 | 0 | schema.format = FORMAT_PROJ_SHAPE; |
1108 | 0 | schema.name = "proj:shape"; |
1109 | |
|
1110 | 0 | auto &sub_schema = *AddAuxSchema(); |
1111 | 0 | projShapeSchemaChildren.push_back(&sub_schema); |
1112 | |
|
1113 | 0 | schema.n_children = projShapeSchemaChildren.size(); |
1114 | 0 | schema.children = projShapeSchemaChildren.data(); |
1115 | 0 | sub_schema.format = ARROW_FORMAT_INT32; |
1116 | 0 | sub_schema.name = "item"; |
1117 | |
|
1118 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1119 | 0 | return nullptr; |
1120 | 0 | } |
1121 | | // "proj:transform" field |
1122 | 0 | { |
1123 | 0 | auto &schema = AddTopSchema(); |
1124 | 0 | static const char FORMAT_PROJ_TRANSFORM[] = { |
1125 | 0 | '+', 'w', ':', '0' + NUM_ITEMS_PROJ_TRANSFORM, 0}; |
1126 | 0 | schema.format = FORMAT_PROJ_TRANSFORM; |
1127 | 0 | schema.name = "proj:transform"; |
1128 | |
|
1129 | 0 | auto &sub_schema = *AddAuxSchema(); |
1130 | 0 | projTransformSchemaChildren.push_back(&sub_schema); |
1131 | |
|
1132 | 0 | schema.n_children = projTransformSchemaChildren.size(); |
1133 | 0 | schema.children = projTransformSchemaChildren.data(); |
1134 | 0 | sub_schema.format = ARROW_FORMAT_FLOAT64; |
1135 | 0 | sub_schema.name = "item"; |
1136 | |
|
1137 | 0 | if (!poLayer->CreateFieldFromArrowSchema(&schema)) |
1138 | 0 | return nullptr; |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | else |
1142 | 0 | { |
1143 | 0 | OGRFieldDefn oLocationField(psOptions->osLocationField.c_str(), |
1144 | 0 | OFTString); |
1145 | 0 | oLocationField.SetWidth(nMaxFieldSize); |
1146 | 0 | if (poLayer->CreateField(&oLocationField) != OGRERR_NONE) |
1147 | 0 | return nullptr; |
1148 | 0 | } |
1149 | | |
1150 | 0 | if (!psOptions->osSrcSRSFieldName.empty()) |
1151 | 0 | { |
1152 | 0 | OGRFieldDefn oSrcSRSField(psOptions->osSrcSRSFieldName.c_str(), |
1153 | 0 | OFTString); |
1154 | 0 | oSrcSRSField.SetWidth(nMaxFieldSize); |
1155 | 0 | if (poLayer->CreateField(&oSrcSRSField) != OGRERR_NONE) |
1156 | 0 | return nullptr; |
1157 | 0 | } |
1158 | 0 | } |
1159 | | |
1160 | 0 | auto poLayerDefn = poLayer->GetLayerDefn(); |
1161 | |
|
1162 | 0 | if (!bIsSTACGeoParquet) |
1163 | 0 | { |
1164 | 0 | for (const auto &oFetchMD : psOptions->aoFetchMD) |
1165 | 0 | { |
1166 | 0 | if (poLayerDefn->GetFieldIndex(oFetchMD.osFieldName.c_str()) < 0) |
1167 | 0 | { |
1168 | 0 | OGRFieldDefn oField(oFetchMD.osFieldName.c_str(), |
1169 | 0 | oFetchMD.eType); |
1170 | 0 | if (poLayer->CreateField(&oField) != OGRERR_NONE) |
1171 | 0 | return nullptr; |
1172 | 0 | } |
1173 | 0 | } |
1174 | 0 | } |
1175 | | |
1176 | 0 | if (bIsSTACGeoParquet) |
1177 | 0 | { |
1178 | 0 | { |
1179 | 0 | auto &geometry = AddTopSchema(); |
1180 | 0 | geometry.format = ARROW_FORMAT_BINARY; |
1181 | 0 | geometry.name = GEOPARQUET_GEOM_COL_NAME; |
1182 | 0 | } |
1183 | |
|
1184 | 0 | for (auto &schema : topSchemas) |
1185 | 0 | topSchemasPointers.push_back(&schema); |
1186 | |
|
1187 | 0 | topSchema.format = ARROW_FORMAT_STRUCT; |
1188 | 0 | topSchema.name = "main"; |
1189 | 0 | topSchema.release = noop_release; |
1190 | 0 | topSchema.n_children = topSchemasPointers.size(); |
1191 | 0 | topSchema.children = topSchemasPointers.data(); |
1192 | 0 | } |
1193 | |
|
1194 | 0 | if (!psOptions->osGTIFilename.empty()) |
1195 | 0 | { |
1196 | 0 | if (!psOptions->aosMetadata.empty()) |
1197 | 0 | { |
1198 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1199 | 0 | "-mo is not supported when -gti_filename is used"); |
1200 | 0 | return nullptr; |
1201 | 0 | } |
1202 | 0 | CPLXMLNode *psRoot = |
1203 | 0 | CPLCreateXMLNode(nullptr, CXT_Element, "GDALTileIndexDataset"); |
1204 | 0 | CPLCreateXMLElementAndValue(psRoot, "IndexDataset", pszDest); |
1205 | 0 | CPLCreateXMLElementAndValue(psRoot, "IndexLayer", poLayer->GetName()); |
1206 | 0 | CPLCreateXMLElementAndValue(psRoot, "LocationField", |
1207 | 0 | psOptions->osLocationField.c_str()); |
1208 | 0 | if (!std::isnan(psOptions->xres)) |
1209 | 0 | { |
1210 | 0 | CPLCreateXMLElementAndValue(psRoot, "ResX", |
1211 | 0 | CPLSPrintf("%.18g", psOptions->xres)); |
1212 | 0 | CPLCreateXMLElementAndValue(psRoot, "ResY", |
1213 | 0 | CPLSPrintf("%.18g", psOptions->yres)); |
1214 | 0 | } |
1215 | 0 | if (!std::isnan(psOptions->xmin)) |
1216 | 0 | { |
1217 | 0 | CPLCreateXMLElementAndValue(psRoot, "MinX", |
1218 | 0 | CPLSPrintf("%.18g", psOptions->xmin)); |
1219 | 0 | CPLCreateXMLElementAndValue(psRoot, "MinY", |
1220 | 0 | CPLSPrintf("%.18g", psOptions->ymin)); |
1221 | 0 | CPLCreateXMLElementAndValue(psRoot, "MaxX", |
1222 | 0 | CPLSPrintf("%.18g", psOptions->xmax)); |
1223 | 0 | CPLCreateXMLElementAndValue(psRoot, "MaxY", |
1224 | 0 | CPLSPrintf("%.18g", psOptions->ymax)); |
1225 | 0 | } |
1226 | |
|
1227 | 0 | int nBandCount = 0; |
1228 | 0 | if (!psOptions->osBandCount.empty()) |
1229 | 0 | { |
1230 | 0 | nBandCount = atoi(psOptions->osBandCount.c_str()); |
1231 | 0 | } |
1232 | 0 | else |
1233 | 0 | { |
1234 | 0 | if (!psOptions->osDataType.empty()) |
1235 | 0 | { |
1236 | 0 | nBandCount = std::max( |
1237 | 0 | nBandCount, |
1238 | 0 | CPLStringList(CSLTokenizeString2( |
1239 | 0 | psOptions->osDataType.c_str(), ", ", 0)) |
1240 | 0 | .size()); |
1241 | 0 | } |
1242 | 0 | if (!psOptions->osNodata.empty()) |
1243 | 0 | { |
1244 | 0 | nBandCount = std::max( |
1245 | 0 | nBandCount, |
1246 | 0 | CPLStringList(CSLTokenizeString2( |
1247 | 0 | psOptions->osNodata.c_str(), ", ", 0)) |
1248 | 0 | .size()); |
1249 | 0 | } |
1250 | 0 | if (!psOptions->osColorInterp.empty()) |
1251 | 0 | { |
1252 | 0 | nBandCount = |
1253 | 0 | std::max(nBandCount, |
1254 | 0 | CPLStringList( |
1255 | 0 | CSLTokenizeString2( |
1256 | 0 | psOptions->osColorInterp.c_str(), ", ", 0)) |
1257 | 0 | .size()); |
1258 | 0 | } |
1259 | 0 | } |
1260 | |
|
1261 | 0 | for (int i = 0; i < nBandCount; ++i) |
1262 | 0 | { |
1263 | 0 | auto psBand = CPLCreateXMLNode(psRoot, CXT_Element, "Band"); |
1264 | 0 | CPLAddXMLAttributeAndValue(psBand, "band", CPLSPrintf("%d", i + 1)); |
1265 | 0 | if (!psOptions->osDataType.empty()) |
1266 | 0 | { |
1267 | 0 | const CPLStringList aosTokens( |
1268 | 0 | CSLTokenizeString2(psOptions->osDataType.c_str(), ", ", 0)); |
1269 | 0 | if (aosTokens.size() == 1) |
1270 | 0 | CPLAddXMLAttributeAndValue(psBand, "dataType", |
1271 | 0 | aosTokens[0]); |
1272 | 0 | else if (i < aosTokens.size()) |
1273 | 0 | CPLAddXMLAttributeAndValue(psBand, "dataType", |
1274 | 0 | aosTokens[i]); |
1275 | 0 | } |
1276 | 0 | if (!psOptions->osNodata.empty()) |
1277 | 0 | { |
1278 | 0 | const CPLStringList aosTokens( |
1279 | 0 | CSLTokenizeString2(psOptions->osNodata.c_str(), ", ", 0)); |
1280 | 0 | if (aosTokens.size() == 1) |
1281 | 0 | CPLCreateXMLElementAndValue(psBand, "NoDataValue", |
1282 | 0 | aosTokens[0]); |
1283 | 0 | else if (i < aosTokens.size()) |
1284 | 0 | CPLCreateXMLElementAndValue(psBand, "NoDataValue", |
1285 | 0 | aosTokens[i]); |
1286 | 0 | } |
1287 | 0 | if (!psOptions->osColorInterp.empty()) |
1288 | 0 | { |
1289 | 0 | const CPLStringList aosTokens(CSLTokenizeString2( |
1290 | 0 | psOptions->osColorInterp.c_str(), ", ", 0)); |
1291 | 0 | if (aosTokens.size() == 1) |
1292 | 0 | CPLCreateXMLElementAndValue(psBand, "ColorInterp", |
1293 | 0 | aosTokens[0]); |
1294 | 0 | else if (i < aosTokens.size()) |
1295 | 0 | CPLCreateXMLElementAndValue(psBand, "ColorInterp", |
1296 | 0 | aosTokens[i]); |
1297 | 0 | } |
1298 | 0 | } |
1299 | |
|
1300 | 0 | if (psOptions->bMaskBand) |
1301 | 0 | { |
1302 | 0 | CPLCreateXMLElementAndValue(psRoot, "MaskBand", "true"); |
1303 | 0 | } |
1304 | 0 | int res = |
1305 | 0 | CPLSerializeXMLTreeToFile(psRoot, psOptions->osGTIFilename.c_str()); |
1306 | 0 | CPLDestroyXMLNode(psRoot); |
1307 | 0 | if (!res) |
1308 | 0 | return nullptr; |
1309 | 0 | } |
1310 | 0 | else |
1311 | 0 | { |
1312 | 0 | if (!bIsSTACGeoParquet) |
1313 | 0 | { |
1314 | 0 | poLayer->SetMetadataItem("LOCATION_FIELD", |
1315 | 0 | psOptions->osLocationField.c_str()); |
1316 | 0 | } |
1317 | 0 | if (!std::isnan(psOptions->xres)) |
1318 | 0 | { |
1319 | 0 | poLayer->SetMetadataItem("RESX", |
1320 | 0 | CPLSPrintf("%.18g", psOptions->xres)); |
1321 | 0 | poLayer->SetMetadataItem("RESY", |
1322 | 0 | CPLSPrintf("%.18g", psOptions->yres)); |
1323 | 0 | } |
1324 | 0 | if (!std::isnan(psOptions->xmin)) |
1325 | 0 | { |
1326 | 0 | poLayer->SetMetadataItem("MINX", |
1327 | 0 | CPLSPrintf("%.18g", psOptions->xmin)); |
1328 | 0 | poLayer->SetMetadataItem("MINY", |
1329 | 0 | CPLSPrintf("%.18g", psOptions->ymin)); |
1330 | 0 | poLayer->SetMetadataItem("MAXX", |
1331 | 0 | CPLSPrintf("%.18g", psOptions->xmax)); |
1332 | 0 | poLayer->SetMetadataItem("MAXY", |
1333 | 0 | CPLSPrintf("%.18g", psOptions->ymax)); |
1334 | 0 | } |
1335 | 0 | if (!psOptions->osBandCount.empty()) |
1336 | 0 | { |
1337 | 0 | poLayer->SetMetadataItem("BAND_COUNT", |
1338 | 0 | psOptions->osBandCount.c_str()); |
1339 | 0 | } |
1340 | 0 | if (!psOptions->osDataType.empty()) |
1341 | 0 | { |
1342 | 0 | poLayer->SetMetadataItem("DATA_TYPE", |
1343 | 0 | psOptions->osDataType.c_str()); |
1344 | 0 | } |
1345 | 0 | if (!psOptions->osNodata.empty()) |
1346 | 0 | { |
1347 | 0 | poLayer->SetMetadataItem("NODATA", psOptions->osNodata.c_str()); |
1348 | 0 | } |
1349 | 0 | if (!psOptions->osColorInterp.empty()) |
1350 | 0 | { |
1351 | 0 | poLayer->SetMetadataItem("COLOR_INTERPRETATION", |
1352 | 0 | psOptions->osColorInterp.c_str()); |
1353 | 0 | } |
1354 | 0 | if (psOptions->bMaskBand) |
1355 | 0 | { |
1356 | 0 | poLayer->SetMetadataItem("MASK_BAND", "YES"); |
1357 | 0 | } |
1358 | 0 | const CPLStringList aosMetadata(psOptions->aosMetadata); |
1359 | 0 | for (const auto &[pszKey, pszValue] : |
1360 | 0 | cpl::IterateNameValue(aosMetadata)) |
1361 | 0 | { |
1362 | 0 | poLayer->SetMetadataItem(pszKey, pszValue); |
1363 | 0 | } |
1364 | 0 | } |
1365 | | |
1366 | 0 | const int ti_field = |
1367 | 0 | poLayerDefn->GetFieldIndex(psOptions->osLocationField.c_str()); |
1368 | 0 | if (!bIsSTACGeoParquet && ti_field < 0) |
1369 | 0 | { |
1370 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1371 | 0 | "Unable to find field `%s' in file `%s'.", |
1372 | 0 | psOptions->osLocationField.c_str(), pszDest); |
1373 | 0 | return nullptr; |
1374 | 0 | } |
1375 | | |
1376 | 0 | int i_SrcSRSName = -1; |
1377 | 0 | if (!bIsSTACGeoParquet && !psOptions->osSrcSRSFieldName.empty()) |
1378 | 0 | { |
1379 | 0 | i_SrcSRSName = |
1380 | 0 | poLayerDefn->GetFieldIndex(psOptions->osSrcSRSFieldName.c_str()); |
1381 | 0 | if (i_SrcSRSName < 0) |
1382 | 0 | { |
1383 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1384 | 0 | "Unable to find field `%s' in file `%s'.", |
1385 | 0 | psOptions->osSrcSRSFieldName.c_str(), pszDest); |
1386 | 0 | return nullptr; |
1387 | 0 | } |
1388 | 0 | } |
1389 | | |
1390 | | // Load in memory existing file names in tile index. |
1391 | 0 | std::set<std::string> oSetExistingFiles; |
1392 | 0 | OGRSpatialReference oAlreadyExistingSRS; |
1393 | 0 | if (bExistingLayer) |
1394 | 0 | { |
1395 | 0 | for (auto &&poFeature : poLayer) |
1396 | 0 | { |
1397 | 0 | if (poFeature->IsFieldSetAndNotNull(ti_field)) |
1398 | 0 | { |
1399 | 0 | if (oSetExistingFiles.empty()) |
1400 | 0 | { |
1401 | 0 | auto poSrcDS = |
1402 | 0 | std::unique_ptr<GDALDataset>(GDALDataset::Open( |
1403 | 0 | poFeature->GetFieldAsString(ti_field), |
1404 | 0 | GDAL_OF_RASTER, nullptr, nullptr, nullptr)); |
1405 | 0 | if (poSrcDS) |
1406 | 0 | { |
1407 | 0 | auto poSrcSRS = poSrcDS->GetSpatialRef(); |
1408 | 0 | if (poSrcSRS) |
1409 | 0 | oAlreadyExistingSRS = *poSrcSRS; |
1410 | 0 | } |
1411 | 0 | } |
1412 | 0 | oSetExistingFiles.insert(poFeature->GetFieldAsString(ti_field)); |
1413 | 0 | } |
1414 | 0 | } |
1415 | 0 | } |
1416 | |
|
1417 | 0 | std::string osCurrentPath; |
1418 | 0 | if (psOptions->bWriteAbsolutePath) |
1419 | 0 | { |
1420 | 0 | char *pszCurrentPath = CPLGetCurrentDir(); |
1421 | 0 | if (pszCurrentPath) |
1422 | 0 | { |
1423 | 0 | osCurrentPath = pszCurrentPath; |
1424 | 0 | } |
1425 | 0 | else |
1426 | 0 | { |
1427 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1428 | 0 | "This system does not support the CPLGetCurrentDir call. " |
1429 | 0 | "The option -bWriteAbsolutePath will have no effect."); |
1430 | 0 | } |
1431 | 0 | CPLFree(pszCurrentPath); |
1432 | 0 | } |
1433 | |
|
1434 | 0 | const bool bIsGTIContext = |
1435 | 0 | !std::isnan(psOptions->xres) || !std::isnan(psOptions->xmin) || |
1436 | 0 | !psOptions->osBandCount.empty() || !psOptions->osNodata.empty() || |
1437 | 0 | !psOptions->osColorInterp.empty() || !psOptions->osDataType.empty() || |
1438 | 0 | psOptions->bMaskBand || !psOptions->aosMetadata.empty() || |
1439 | 0 | !psOptions->osGTIFilename.empty(); |
1440 | | |
1441 | | /* -------------------------------------------------------------------- */ |
1442 | | /* loop over GDAL files, processing. */ |
1443 | | /* -------------------------------------------------------------------- */ |
1444 | 0 | ArrowArray topArray{}; |
1445 | |
|
1446 | 0 | struct TopArrayReleaser |
1447 | 0 | { |
1448 | 0 | ArrowArray *m_array = nullptr; |
1449 | |
|
1450 | 0 | explicit TopArrayReleaser(ArrowArray *array) : m_array(array) |
1451 | 0 | { |
1452 | 0 | } |
1453 | |
|
1454 | 0 | ~TopArrayReleaser() |
1455 | 0 | { |
1456 | 0 | if (m_array && m_array->release) |
1457 | 0 | m_array->release(m_array); |
1458 | 0 | } |
1459 | |
|
1460 | 0 | TopArrayReleaser(const TopArrayReleaser &) = delete; |
1461 | 0 | TopArrayReleaser &operator=(const TopArrayReleaser &) = delete; |
1462 | 0 | }; |
1463 | |
|
1464 | 0 | TopArrayReleaser arrayReleaser(&topArray); |
1465 | |
|
1466 | 0 | ArrowArray **topArrays = nullptr; |
1467 | |
|
1468 | 0 | int iArray = 0; |
1469 | 0 | const int iIdArray = iArray++; |
1470 | |
|
1471 | 0 | const int iStacExtensionsArray = iArray++; |
1472 | 0 | ArrowArray *stacExtensionSubArray = nullptr; |
1473 | 0 | uint32_t nStacExtensionSubArrayMaxAlloc = 0; |
1474 | |
|
1475 | 0 | const int iLinksArray = iArray++; |
1476 | 0 | ArrowArray *linksItemArray = nullptr; |
1477 | |
|
1478 | 0 | const int iAssetsArray = iArray++; |
1479 | 0 | ArrowArray *imageArray = nullptr; |
1480 | 0 | uint32_t nImageHrefArrayMaxAlloc = 0; |
1481 | 0 | ArrowArray *imageHrefArray = nullptr; |
1482 | 0 | ArrowArray *imageRoleArray = nullptr; |
1483 | 0 | ArrowArray *imageRoleItemArray = nullptr; |
1484 | 0 | uint32_t nImageRoleItemArrayMaxAlloc = 0; |
1485 | 0 | ArrowArray *imageTitleArray = nullptr; |
1486 | 0 | uint32_t nImageTitleArrayMaxAlloc = 0; |
1487 | 0 | ArrowArray *imageTypeArray = nullptr; |
1488 | 0 | uint32_t nImageTypeArrayMaxAlloc = 0; |
1489 | |
|
1490 | 0 | const int iBandsArray = iArray++; |
1491 | 0 | uint32_t nBandsItemCount = 0; |
1492 | 0 | uint32_t nBandsItemAlloc = 0; |
1493 | 0 | ArrowArray *bandsItemArray = nullptr; |
1494 | 0 | ArrowArray *bandsNameArray = nullptr; |
1495 | 0 | uint32_t nBandsNameArrayMaxAlloc = 0; |
1496 | 0 | ArrowArray *bandsCommonNameArray = nullptr; |
1497 | 0 | uint32_t nBandsCommonNameArrayMaxAlloc = 0; |
1498 | 0 | ArrowArray *bandsCenterWavelengthArray = nullptr; |
1499 | 0 | uint32_t nBandsCenterWavelengthArrayMaxAlloc = 0; |
1500 | 0 | ArrowArray *bandsFWHMArray = nullptr; |
1501 | 0 | uint32_t nBandsFWHMArrayMaxAlloc = 0; |
1502 | 0 | ArrowArray *bandsNodataArray = nullptr; |
1503 | 0 | uint32_t nBandsNodataArrayMaxAlloc = 0; |
1504 | 0 | ArrowArray *bandsDataTypeArray = nullptr; |
1505 | 0 | uint32_t nBandsDataTypeArrayMaxAlloc = 0; |
1506 | 0 | ArrowArray *bandsUnitArray = nullptr; |
1507 | 0 | uint32_t nBandsUnitArrayMaxAlloc = 0; |
1508 | |
|
1509 | 0 | const int iProjCode = iArray++; |
1510 | 0 | uint32_t nProjCodeArrayMaxAlloc = 0; |
1511 | 0 | const int iProjWKT2 = iArray++; |
1512 | 0 | uint32_t nProjWKT2ArrayMaxAlloc = 0; |
1513 | 0 | const int iProjPROJJSON = iArray++; |
1514 | 0 | uint32_t nProjPROJJSONArrayMaxAlloc = 0; |
1515 | 0 | const int iProjBBOX = iArray++; |
1516 | 0 | ArrowArray *projBBOXItems = nullptr; |
1517 | 0 | const int iProjShape = iArray++; |
1518 | 0 | ArrowArray *projShapeItems = nullptr; |
1519 | 0 | const int iProjTransform = iArray++; |
1520 | 0 | ArrowArray *projTransformItems = nullptr; |
1521 | |
|
1522 | 0 | const int iWkbArray = iArray++; |
1523 | |
|
1524 | 0 | std::unique_ptr<OGRArrowArrayHelper> arrayHelper; |
1525 | |
|
1526 | 0 | const auto InitTopArray = |
1527 | 0 | [iIdArray, iStacExtensionsArray, iLinksArray, iAssetsArray, iBandsArray, |
1528 | 0 | iProjCode, iProjWKT2, iProjPROJJSON, iProjBBOX, iProjShape, |
1529 | 0 | iProjTransform, iWkbArray, nMaxBatchSize, &arrayHelper, &topArray, |
1530 | 0 | &topArrays, &topSchema, &nStacExtensionSubArrayMaxAlloc, |
1531 | 0 | &stacExtensionSubArray, &linksItemArray, &imageArray, |
1532 | 0 | &nImageHrefArrayMaxAlloc, &imageHrefArray, &imageRoleArray, |
1533 | 0 | &imageRoleItemArray, &nImageRoleItemArrayMaxAlloc, &imageTitleArray, |
1534 | 0 | &imageTypeArray, &nImageTitleArrayMaxAlloc, &nImageTypeArrayMaxAlloc, |
1535 | 0 | &nBandsItemCount, &nBandsItemAlloc, &bandsItemArray, &bandsNameArray, |
1536 | 0 | &nBandsNameArrayMaxAlloc, &bandsCommonNameArray, |
1537 | 0 | &nBandsCommonNameArrayMaxAlloc, &nBandsCenterWavelengthArrayMaxAlloc, |
1538 | 0 | &bandsCenterWavelengthArray, &nBandsFWHMArrayMaxAlloc, &bandsFWHMArray, |
1539 | 0 | &bandsNodataArray, &nBandsNodataArrayMaxAlloc, &bandsDataTypeArray, |
1540 | 0 | &nBandsDataTypeArrayMaxAlloc, &bandsUnitArray, |
1541 | 0 | &nBandsUnitArrayMaxAlloc, &nProjCodeArrayMaxAlloc, |
1542 | 0 | &nProjWKT2ArrayMaxAlloc, &nProjPROJJSONArrayMaxAlloc, &projBBOXItems, |
1543 | 0 | &projShapeItems, &projTransformItems]() |
1544 | 0 | { |
1545 | 0 | const auto AllocArray = []() |
1546 | 0 | { |
1547 | 0 | auto array = |
1548 | 0 | static_cast<ArrowArray *>(CPLCalloc(1, sizeof(ArrowArray))); |
1549 | 0 | array->release = ReleaseArray; |
1550 | 0 | return array; |
1551 | 0 | }; |
1552 | |
|
1553 | 0 | const auto AllocNBuffers = [](ArrowArray &array, int n_buffers) |
1554 | 0 | { |
1555 | 0 | array.n_buffers = n_buffers; |
1556 | 0 | array.buffers = static_cast<const void **>( |
1557 | 0 | CPLCalloc(n_buffers, sizeof(const void *))); |
1558 | 0 | }; |
1559 | |
|
1560 | 0 | const auto AllocNArrays = [](ArrowArray &array, int n_children) |
1561 | 0 | { |
1562 | 0 | array.n_children = n_children; |
1563 | 0 | array.children = static_cast<ArrowArray **>( |
1564 | 0 | CPLCalloc(n_children, sizeof(ArrowArray *))); |
1565 | 0 | }; |
1566 | |
|
1567 | 0 | const auto InitializePrimitiveArray = |
1568 | 0 | [&AllocNBuffers](ArrowArray &sArray, size_t nEltSize, |
1569 | 0 | size_t nLength) |
1570 | 0 | { |
1571 | 0 | AllocNBuffers(sArray, 2); |
1572 | 0 | sArray.buffers[ARROW_BUF_DATA] = |
1573 | 0 | static_cast<const void *>(CPLCalloc(nLength, nEltSize)); |
1574 | 0 | }; |
1575 | |
|
1576 | 0 | const auto InitializeStringOrBinaryArray = |
1577 | 0 | [&AllocNBuffers](ArrowArray &sArray, size_t nLength) |
1578 | 0 | { |
1579 | 0 | AllocNBuffers(sArray, 3); |
1580 | | // +1 since the length of string of idx i is given by |
1581 | | // offset[i+1] - offset[i] |
1582 | 0 | sArray.buffers[ARROW_BUF_DATA] = static_cast<const void *>( |
1583 | 0 | CPLCalloc(nLength + 1, sizeof(uint32_t))); |
1584 | | // Allocate a minimum amount to not get a null pointer |
1585 | 0 | sArray.buffers[ARROW_BUF_BYTES] = |
1586 | 0 | static_cast<const void *>(CPLCalloc(1, 1)); |
1587 | 0 | }; |
1588 | |
|
1589 | 0 | const auto InitializeListArray = |
1590 | 0 | [&AllocNBuffers, &AllocNArrays]( |
1591 | 0 | ArrowArray &sArray, ArrowArray *subArray, size_t nLength) |
1592 | 0 | { |
1593 | 0 | AllocNBuffers(sArray, 2); |
1594 | 0 | sArray.buffers[ARROW_BUF_DATA] = static_cast<const void *>( |
1595 | 0 | CPLCalloc(nLength + 1, sizeof(uint32_t))); |
1596 | 0 | AllocNArrays(sArray, 1); |
1597 | 0 | sArray.children[0] = subArray; |
1598 | 0 | }; |
1599 | |
|
1600 | 0 | const auto InitializeFixedSizeListArray = |
1601 | 0 | [&AllocNBuffers, |
1602 | 0 | &AllocNArrays](ArrowArray &sArray, ArrowArray *subArray, |
1603 | 0 | size_t nItemSize, size_t nItemCount, size_t nLength) |
1604 | 0 | { |
1605 | 0 | AllocNArrays(sArray, 1); |
1606 | 0 | AllocNBuffers(sArray, 1); |
1607 | 0 | sArray.children[0] = subArray; |
1608 | 0 | AllocNBuffers(*subArray, 2); |
1609 | 0 | subArray->buffers[ARROW_BUF_DATA] = static_cast<const void *>( |
1610 | 0 | CPLCalloc(nItemCount * nItemSize, nLength)); |
1611 | 0 | }; |
1612 | |
|
1613 | 0 | const auto InitializeStructArray = [&AllocNBuffers](ArrowArray &sArray) |
1614 | 0 | { AllocNBuffers(sArray, 1); }; |
1615 | |
|
1616 | 0 | topArrays = static_cast<ArrowArray **>(CPLCalloc( |
1617 | 0 | static_cast<int>(topSchema.n_children), sizeof(ArrowArray *))); |
1618 | 0 | for (int i = 0; i < static_cast<int>(topSchema.n_children); ++i) |
1619 | 0 | topArrays[i] = AllocArray(); |
1620 | |
|
1621 | 0 | topArray = ArrowArray{}; |
1622 | 0 | topArray.release = ReleaseArray; |
1623 | 0 | topArray.n_children = topSchema.n_children; |
1624 | 0 | topArray.children = topArrays; |
1625 | 0 | InitializeStructArray(topArray); |
1626 | |
|
1627 | 0 | InitializeStringOrBinaryArray(*topArrays[iIdArray], nMaxBatchSize); |
1628 | |
|
1629 | 0 | stacExtensionSubArray = AllocArray(); |
1630 | 0 | nStacExtensionSubArrayMaxAlloc = 0; |
1631 | 0 | { |
1632 | 0 | auto *array = topArrays[iStacExtensionsArray]; |
1633 | 0 | InitializeListArray(*array, stacExtensionSubArray, nMaxBatchSize); |
1634 | 0 | InitializeStringOrBinaryArray( |
1635 | 0 | *stacExtensionSubArray, COUNT_STAC_EXTENSIONS * nMaxBatchSize); |
1636 | 0 | } |
1637 | |
|
1638 | 0 | linksItemArray = AllocArray(); |
1639 | 0 | { |
1640 | 0 | auto *array = topArrays[iLinksArray]; |
1641 | 0 | InitializeListArray(*array, linksItemArray, nMaxBatchSize); |
1642 | 0 | InitializeStructArray(*linksItemArray); |
1643 | |
|
1644 | 0 | AllocNArrays(*linksItemArray, 4); |
1645 | 0 | ArrowArray *linksHrefArray = AllocArray(); |
1646 | 0 | ArrowArray *linksRelArray = AllocArray(); |
1647 | 0 | ArrowArray *linksTypeArray = AllocArray(); |
1648 | 0 | ArrowArray *linksTitleArray = AllocArray(); |
1649 | 0 | linksItemArray->children[0] = linksHrefArray; |
1650 | 0 | linksItemArray->children[1] = linksRelArray; |
1651 | 0 | linksItemArray->children[2] = linksTypeArray; |
1652 | 0 | linksItemArray->children[3] = linksTitleArray; |
1653 | 0 | InitializeStringOrBinaryArray(*linksHrefArray, nMaxBatchSize); |
1654 | 0 | InitializeStringOrBinaryArray(*linksRelArray, nMaxBatchSize); |
1655 | 0 | InitializeStringOrBinaryArray(*linksTypeArray, nMaxBatchSize); |
1656 | 0 | InitializeStringOrBinaryArray(*linksTitleArray, nMaxBatchSize); |
1657 | 0 | } |
1658 | |
|
1659 | 0 | imageArray = AllocArray(); |
1660 | 0 | nImageHrefArrayMaxAlloc = 0; |
1661 | 0 | imageHrefArray = AllocArray(); |
1662 | 0 | imageRoleArray = AllocArray(); |
1663 | 0 | imageRoleItemArray = AllocArray(); |
1664 | 0 | nImageHrefArrayMaxAlloc = 0; |
1665 | 0 | nImageRoleItemArrayMaxAlloc = 0; |
1666 | 0 | imageTitleArray = AllocArray(); |
1667 | 0 | nImageTitleArrayMaxAlloc = 0; |
1668 | 0 | imageTypeArray = AllocArray(); |
1669 | 0 | nImageTypeArrayMaxAlloc = 0; |
1670 | 0 | { |
1671 | 0 | auto *assets = topArrays[iAssetsArray]; |
1672 | 0 | InitializeStructArray(*assets); |
1673 | 0 | AllocNArrays(*assets, 1); |
1674 | 0 | assets->children[0] = imageArray; |
1675 | |
|
1676 | 0 | InitializeStructArray(*imageArray); |
1677 | 0 | AllocNArrays(*imageArray, 4); |
1678 | 0 | imageArray->children[0] = imageHrefArray; |
1679 | 0 | imageArray->children[1] = imageRoleArray; |
1680 | 0 | imageArray->children[2] = imageTitleArray; |
1681 | 0 | imageArray->children[3] = imageTypeArray; |
1682 | |
|
1683 | 0 | InitializeStringOrBinaryArray(*imageHrefArray, nMaxBatchSize); |
1684 | 0 | InitializeStringOrBinaryArray(*imageTitleArray, nMaxBatchSize); |
1685 | 0 | InitializeStringOrBinaryArray(*imageTypeArray, nMaxBatchSize); |
1686 | 0 | InitializeListArray(*imageRoleArray, imageRoleItemArray, |
1687 | 0 | nMaxBatchSize); |
1688 | 0 | InitializeStringOrBinaryArray(*imageRoleItemArray, nMaxBatchSize); |
1689 | 0 | } |
1690 | | |
1691 | | // "bands" related initialization |
1692 | 0 | { |
1693 | 0 | nBandsItemCount = 0; |
1694 | 0 | nBandsItemAlloc = 0; |
1695 | 0 | bandsItemArray = AllocArray(); |
1696 | 0 | InitializeListArray(*(topArrays[iBandsArray]), bandsItemArray, |
1697 | 0 | nMaxBatchSize); |
1698 | 0 | InitializeStructArray(*bandsItemArray); |
1699 | |
|
1700 | 0 | bandsNameArray = AllocArray(); |
1701 | 0 | InitializeStringOrBinaryArray(*bandsNameArray, 0); |
1702 | 0 | nBandsNameArrayMaxAlloc = 0; |
1703 | |
|
1704 | 0 | bandsCommonNameArray = AllocArray(); |
1705 | 0 | InitializeStringOrBinaryArray(*bandsCommonNameArray, 0); |
1706 | 0 | nBandsCommonNameArrayMaxAlloc = 0; |
1707 | |
|
1708 | 0 | bandsCenterWavelengthArray = AllocArray(); |
1709 | 0 | InitializePrimitiveArray(*bandsCenterWavelengthArray, sizeof(float), |
1710 | 0 | 1); |
1711 | 0 | nBandsCenterWavelengthArrayMaxAlloc = 0; |
1712 | |
|
1713 | 0 | bandsFWHMArray = AllocArray(); |
1714 | 0 | InitializePrimitiveArray(*bandsFWHMArray, sizeof(float), 1); |
1715 | 0 | nBandsFWHMArrayMaxAlloc = 0; |
1716 | |
|
1717 | 0 | bandsNodataArray = AllocArray(); |
1718 | 0 | InitializeStringOrBinaryArray(*bandsNodataArray, 0); |
1719 | 0 | nBandsNodataArrayMaxAlloc = 0; |
1720 | |
|
1721 | 0 | bandsDataTypeArray = AllocArray(); |
1722 | 0 | InitializeStringOrBinaryArray(*bandsDataTypeArray, 0); |
1723 | 0 | nBandsDataTypeArrayMaxAlloc = 0; |
1724 | |
|
1725 | 0 | bandsUnitArray = AllocArray(); |
1726 | 0 | InitializeStringOrBinaryArray(*bandsUnitArray, 0); |
1727 | 0 | nBandsUnitArrayMaxAlloc = 0; |
1728 | |
|
1729 | 0 | AllocNArrays(*bandsItemArray, 7); |
1730 | 0 | bandsItemArray->children[0] = bandsNameArray; |
1731 | 0 | bandsItemArray->children[1] = bandsCommonNameArray; |
1732 | 0 | bandsItemArray->children[2] = bandsCenterWavelengthArray; |
1733 | 0 | bandsItemArray->children[3] = bandsFWHMArray; |
1734 | 0 | bandsItemArray->children[4] = bandsNodataArray; |
1735 | 0 | bandsItemArray->children[5] = bandsDataTypeArray; |
1736 | 0 | bandsItemArray->children[6] = bandsUnitArray; |
1737 | 0 | } |
1738 | | |
1739 | | // proj:xxxx related initializations |
1740 | 0 | { |
1741 | 0 | InitializeStringOrBinaryArray(*topArrays[iProjCode], nMaxBatchSize); |
1742 | 0 | nProjCodeArrayMaxAlloc = 0; |
1743 | 0 | InitializeStringOrBinaryArray(*topArrays[iProjWKT2], nMaxBatchSize); |
1744 | 0 | nProjWKT2ArrayMaxAlloc = 0; |
1745 | 0 | InitializeStringOrBinaryArray(*topArrays[iProjPROJJSON], |
1746 | 0 | nMaxBatchSize); |
1747 | 0 | nProjPROJJSONArrayMaxAlloc = 0; |
1748 | |
|
1749 | 0 | projBBOXItems = AllocArray(); |
1750 | 0 | InitializeFixedSizeListArray(*(topArrays[iProjBBOX]), projBBOXItems, |
1751 | 0 | sizeof(double), NUM_ITEMS_PROJ_BBOX, |
1752 | 0 | nMaxBatchSize); |
1753 | |
|
1754 | 0 | projShapeItems = AllocArray(); |
1755 | 0 | InitializeFixedSizeListArray(*(topArrays[iProjShape]), |
1756 | 0 | projShapeItems, sizeof(int32_t), |
1757 | 0 | NUM_ITEMS_PROJ_SHAPE, nMaxBatchSize); |
1758 | |
|
1759 | 0 | projTransformItems = AllocArray(); |
1760 | 0 | InitializeFixedSizeListArray( |
1761 | 0 | *(topArrays[iProjTransform]), projTransformItems, |
1762 | 0 | sizeof(double), NUM_ITEMS_PROJ_TRANSFORM, nMaxBatchSize); |
1763 | 0 | } |
1764 | |
|
1765 | 0 | InitializeStringOrBinaryArray(*topArrays[iWkbArray], nMaxBatchSize); |
1766 | |
|
1767 | 0 | arrayHelper = |
1768 | 0 | std::make_unique<OGRArrowArrayHelper>(&topArray, nMaxBatchSize); |
1769 | 0 | }; |
1770 | |
|
1771 | 0 | int nBatchSize = 0; |
1772 | |
|
1773 | 0 | const auto FlushArrays = [poLayer, &topArray, &linksItemArray, &imageArray, |
1774 | 0 | &topSchema, &nBatchSize, &arrayHelper]() |
1775 | 0 | { |
1776 | 0 | topArray.length = nBatchSize; |
1777 | 0 | linksItemArray->length = nBatchSize; |
1778 | 0 | imageArray->length = nBatchSize; |
1779 | 0 | for (int i = 0; i < static_cast<int>(topArray.n_children); ++i) |
1780 | 0 | topArray.children[i]->length = nBatchSize; |
1781 | 0 | const bool ret = poLayer->WriteArrowBatch(&topSchema, &topArray); |
1782 | 0 | if (topArray.release) |
1783 | 0 | { |
1784 | 0 | topArray.release(&topArray); |
1785 | 0 | } |
1786 | 0 | memset(&topArray, 0, sizeof(topArray)); |
1787 | 0 | nBatchSize = 0; |
1788 | 0 | arrayHelper.reset(); |
1789 | 0 | return ret; |
1790 | 0 | }; |
1791 | |
|
1792 | 0 | int iCur = 0; |
1793 | 0 | int nTotal = nSrcCount + 1; |
1794 | 0 | while (true) |
1795 | 0 | { |
1796 | 0 | const std::string osSrcFilename = oGDALTileIndexTileIterator.next(); |
1797 | 0 | if (osSrcFilename.empty()) |
1798 | 0 | break; |
1799 | 0 | if (bSkipFirstTile) |
1800 | 0 | { |
1801 | 0 | bSkipFirstTile = false; |
1802 | 0 | continue; |
1803 | 0 | } |
1804 | | |
1805 | 0 | std::string osFileNameToWrite; |
1806 | 0 | VSIStatBuf sStatBuf; |
1807 | | |
1808 | | // Make sure it is a file before building absolute path name. |
1809 | 0 | if (!osCurrentPath.empty() && |
1810 | 0 | CPLIsFilenameRelative(osSrcFilename.c_str()) && |
1811 | 0 | VSIStat(osSrcFilename.c_str(), &sStatBuf) == 0) |
1812 | 0 | { |
1813 | 0 | osFileNameToWrite = CPLProjectRelativeFilenameSafe( |
1814 | 0 | osCurrentPath.c_str(), osSrcFilename.c_str()); |
1815 | 0 | } |
1816 | 0 | else |
1817 | 0 | { |
1818 | 0 | osFileNameToWrite = osSrcFilename.c_str(); |
1819 | 0 | } |
1820 | | |
1821 | | // Checks that file is not already in tileindex. |
1822 | 0 | if (oSetExistingFiles.find(osFileNameToWrite) != |
1823 | 0 | oSetExistingFiles.end()) |
1824 | 0 | { |
1825 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1826 | 0 | "File %s is already in tileindex. Skipping it.", |
1827 | 0 | osFileNameToWrite.c_str()); |
1828 | 0 | continue; |
1829 | 0 | } |
1830 | | |
1831 | 0 | std::unique_ptr<GDALDataset> poSrcDS; |
1832 | 0 | { |
1833 | 0 | std::unique_ptr<CPLTurnFailureIntoWarningBackuper> |
1834 | 0 | poFailureIntoWarning; |
1835 | 0 | if (!bFailOnErrors) |
1836 | 0 | poFailureIntoWarning = |
1837 | 0 | std::make_unique<CPLTurnFailureIntoWarningBackuper>(); |
1838 | 0 | CPL_IGNORE_RET_VAL(poFailureIntoWarning); |
1839 | |
|
1840 | 0 | poSrcDS.reset(GDALDataset::Open( |
1841 | 0 | osSrcFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, |
1842 | 0 | nullptr, nullptr, nullptr)); |
1843 | 0 | if (poSrcDS == nullptr) |
1844 | 0 | { |
1845 | 0 | CPLError(bFailOnErrors ? CE_Failure : CE_Warning, |
1846 | 0 | CPLE_AppDefined, "Unable to open %s%s.", |
1847 | 0 | osSrcFilename.c_str(), |
1848 | 0 | bFailOnErrors ? "" : ", skipping"); |
1849 | 0 | if (bFailOnErrors) |
1850 | 0 | return nullptr; |
1851 | 0 | continue; |
1852 | 0 | } |
1853 | 0 | } |
1854 | | |
1855 | 0 | GDALGeoTransform gt; |
1856 | 0 | if (poSrcDS->GetGeoTransform(gt) != CE_None) |
1857 | 0 | { |
1858 | 0 | CPLError(bFailOnErrors ? CE_Failure : CE_Warning, CPLE_AppDefined, |
1859 | 0 | "It appears no georeferencing is available for\n" |
1860 | 0 | "`%s'%s.", |
1861 | 0 | osSrcFilename.c_str(), bFailOnErrors ? "" : ", skipping"); |
1862 | 0 | if (bFailOnErrors) |
1863 | 0 | return nullptr; |
1864 | 0 | continue; |
1865 | 0 | } |
1866 | | |
1867 | 0 | auto poSrcSRS = poSrcDS->GetSpatialRef(); |
1868 | | // If not set target srs, test that the current file uses same |
1869 | | // projection as others. |
1870 | 0 | if (oTargetSRS.IsEmpty()) |
1871 | 0 | { |
1872 | 0 | if (!oAlreadyExistingSRS.IsEmpty()) |
1873 | 0 | { |
1874 | 0 | if (poSrcSRS == nullptr || |
1875 | 0 | !poSrcSRS->IsSame(&oAlreadyExistingSRS)) |
1876 | 0 | { |
1877 | 0 | CPLError( |
1878 | 0 | CE_Warning, CPLE_AppDefined, |
1879 | 0 | "%s is not using the same projection system " |
1880 | 0 | "as other files in the tileindex.\n" |
1881 | 0 | "This may cause problems when using it in MapServer " |
1882 | 0 | "for example.\n" |
1883 | 0 | "Use -t_srs option to set target projection system. %s", |
1884 | 0 | osSrcFilename.c_str(), |
1885 | 0 | psOptions->bSkipDifferentProjection |
1886 | 0 | ? "Skipping this file." |
1887 | 0 | : ""); |
1888 | 0 | if (psOptions->bSkipDifferentProjection) |
1889 | 0 | { |
1890 | 0 | continue; |
1891 | 0 | } |
1892 | 0 | } |
1893 | 0 | } |
1894 | 0 | else |
1895 | 0 | { |
1896 | 0 | if (poSrcSRS) |
1897 | 0 | oAlreadyExistingSRS = *poSrcSRS; |
1898 | 0 | } |
1899 | 0 | } |
1900 | | |
1901 | 0 | const int nXSize = poSrcDS->GetRasterXSize(); |
1902 | 0 | const int nYSize = poSrcDS->GetRasterYSize(); |
1903 | 0 | if (nXSize == 0 || nYSize == 0) |
1904 | 0 | { |
1905 | 0 | CPLError(bFailOnErrors ? CE_Failure : CE_Warning, CPLE_AppDefined, |
1906 | 0 | "%s has 0 width or height%s", osSrcFilename.c_str(), |
1907 | 0 | bFailOnErrors ? "" : ", skipping"); |
1908 | 0 | if (bFailOnErrors) |
1909 | 0 | return nullptr; |
1910 | 0 | continue; |
1911 | 0 | } |
1912 | | |
1913 | 0 | double adfX[5] = {0.0, 0.0, 0.0, 0.0, 0.0}; |
1914 | 0 | double adfY[5] = {0.0, 0.0, 0.0, 0.0, 0.0}; |
1915 | 0 | adfX[0] = gt.xorig + 0 * gt.xscale + 0 * gt.xrot; |
1916 | 0 | adfY[0] = gt.yorig + 0 * gt.yrot + 0 * gt.yscale; |
1917 | |
|
1918 | 0 | adfX[1] = gt.xorig + nXSize * gt.xscale + 0 * gt.xrot; |
1919 | 0 | adfY[1] = gt.yorig + nXSize * gt.yrot + 0 * gt.yscale; |
1920 | |
|
1921 | 0 | adfX[2] = gt.xorig + nXSize * gt.xscale + nYSize * gt.xrot; |
1922 | 0 | adfY[2] = gt.yorig + nXSize * gt.yrot + nYSize * gt.yscale; |
1923 | |
|
1924 | 0 | adfX[3] = gt.xorig + 0 * gt.xscale + nYSize * gt.xrot; |
1925 | 0 | adfY[3] = gt.yorig + 0 * gt.yrot + nYSize * gt.yscale; |
1926 | |
|
1927 | 0 | adfX[4] = gt.xorig + 0 * gt.xscale + 0 * gt.xrot; |
1928 | 0 | adfY[4] = gt.yorig + 0 * gt.yrot + 0 * gt.yscale; |
1929 | |
|
1930 | 0 | const double dfMinXBeforeReproj = |
1931 | 0 | std::min(std::min(adfX[0], adfX[1]), std::min(adfX[2], adfX[3])); |
1932 | 0 | const double dfMinYBeforeReproj = |
1933 | 0 | std::min(std::min(adfY[0], adfY[1]), std::min(adfY[2], adfY[3])); |
1934 | 0 | const double dfMaxXBeforeReproj = |
1935 | 0 | std::max(std::max(adfX[0], adfX[1]), std::max(adfX[2], adfX[3])); |
1936 | 0 | const double dfMaxYBeforeReproj = |
1937 | 0 | std::max(std::max(adfY[0], adfY[1]), std::max(adfY[2], adfY[3])); |
1938 | | |
1939 | | // If set target srs, do the forward transformation of all points. |
1940 | 0 | if (!oTargetSRS.IsEmpty() && poSrcSRS) |
1941 | 0 | { |
1942 | 0 | if (!poSrcSRS->IsSame(&oTargetSRS)) |
1943 | 0 | { |
1944 | 0 | auto poCT = std::unique_ptr<OGRCoordinateTransformation>( |
1945 | 0 | OGRCreateCoordinateTransformation(poSrcSRS, &oTargetSRS)); |
1946 | 0 | if (!poCT || !poCT->Transform(5, adfX, adfY, nullptr)) |
1947 | 0 | { |
1948 | 0 | CPLError(bFailOnErrors ? CE_Failure : CE_Warning, |
1949 | 0 | CPLE_AppDefined, |
1950 | 0 | "unable to transform points from source " |
1951 | 0 | "SRS `%s' to target SRS `%s' for file `%s'%s", |
1952 | 0 | poSrcDS->GetProjectionRef(), |
1953 | 0 | psOptions->osTargetSRS.c_str(), |
1954 | 0 | osFileNameToWrite.c_str(), |
1955 | 0 | bFailOnErrors ? "" : ", skipping"); |
1956 | 0 | if (bFailOnErrors) |
1957 | 0 | return nullptr; |
1958 | 0 | continue; |
1959 | 0 | } |
1960 | 0 | } |
1961 | 0 | } |
1962 | 0 | else if (bIsGTIContext && !oAlreadyExistingSRS.IsEmpty() && |
1963 | 0 | (poSrcSRS == nullptr || |
1964 | 0 | !poSrcSRS->IsSame(&oAlreadyExistingSRS))) |
1965 | 0 | { |
1966 | 0 | CPLError( |
1967 | 0 | CE_Failure, CPLE_AppDefined, |
1968 | 0 | "%s is not using the same projection system " |
1969 | 0 | "as other files in the tileindex. This is not compatible of " |
1970 | 0 | "GTI use. Use -t_srs option to reproject tile extents " |
1971 | 0 | "to a common SRS.", |
1972 | 0 | osSrcFilename.c_str()); |
1973 | 0 | return nullptr; |
1974 | 0 | } |
1975 | | |
1976 | 0 | const double dfMinX = |
1977 | 0 | std::min(std::min(adfX[0], adfX[1]), std::min(adfX[2], adfX[3])); |
1978 | 0 | const double dfMinY = |
1979 | 0 | std::min(std::min(adfY[0], adfY[1]), std::min(adfY[2], adfY[3])); |
1980 | 0 | const double dfMaxX = |
1981 | 0 | std::max(std::max(adfX[0], adfX[1]), std::max(adfX[2], adfX[3])); |
1982 | 0 | const double dfMaxY = |
1983 | 0 | std::max(std::max(adfY[0], adfY[1]), std::max(adfY[2], adfY[3])); |
1984 | 0 | const double dfRes = |
1985 | 0 | sqrt((dfMaxX - dfMinX) * (dfMaxY - dfMinY) / nXSize / nYSize); |
1986 | 0 | if (!std::isnan(psOptions->dfMinPixelSize) && |
1987 | 0 | dfRes < psOptions->dfMinPixelSize) |
1988 | 0 | { |
1989 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1990 | 0 | "%s has %f as pixel size (< %f). Skipping", |
1991 | 0 | osSrcFilename.c_str(), dfRes, psOptions->dfMinPixelSize); |
1992 | 0 | continue; |
1993 | 0 | } |
1994 | 0 | if (!std::isnan(psOptions->dfMaxPixelSize) && |
1995 | 0 | dfRes > psOptions->dfMaxPixelSize) |
1996 | 0 | { |
1997 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1998 | 0 | "%s has %f as pixel size (> %f). Skipping", |
1999 | 0 | osSrcFilename.c_str(), dfRes, psOptions->dfMaxPixelSize); |
2000 | 0 | continue; |
2001 | 0 | } |
2002 | | |
2003 | 0 | auto poPoly = std::make_unique<OGRPolygon>(); |
2004 | 0 | auto poRing = std::make_unique<OGRLinearRing>(); |
2005 | 0 | for (int k = 0; k < 5; k++) |
2006 | 0 | poRing->addPoint(adfX[k], adfY[k]); |
2007 | 0 | poPoly->addRing(std::move(poRing)); |
2008 | |
|
2009 | 0 | if (bIsSTACGeoParquet) |
2010 | 0 | { |
2011 | 0 | const char *pszDriverName = poSrcDS->GetDriverName(); |
2012 | 0 | if (pszDriverName && EQUAL(pszDriverName, "MEM")) |
2013 | 0 | { |
2014 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2015 | 0 | "Memory datasets cannot be referenced in a " |
2016 | 0 | "STAC-GeoParquet catalog"); |
2017 | 0 | return nullptr; |
2018 | 0 | } |
2019 | | |
2020 | 0 | if (!arrayHelper) |
2021 | 0 | { |
2022 | 0 | InitTopArray(); |
2023 | 0 | } |
2024 | | |
2025 | | // Write "id" |
2026 | 0 | { |
2027 | 0 | std::string osId(CPLGetFilename(osFileNameToWrite.c_str())); |
2028 | |
|
2029 | 0 | if (psOptions->osIdMethod == "md5") |
2030 | 0 | { |
2031 | 0 | const std::string osFilename = |
2032 | 0 | VSIFileManager::GetHandler(osFileNameToWrite.c_str()) |
2033 | 0 | ->GetStreamingFilename(osFileNameToWrite); |
2034 | 0 | auto fp = VSIFilesystemHandler::OpenStatic( |
2035 | 0 | osFilename.c_str(), "rb"); |
2036 | 0 | if (!fp) |
2037 | 0 | { |
2038 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", |
2039 | 0 | osFileNameToWrite.c_str()); |
2040 | 0 | return nullptr; |
2041 | 0 | } |
2042 | 0 | CPLMD5Context md5Context; |
2043 | 0 | CPLMD5Init(&md5Context); |
2044 | 0 | constexpr size_t CHUNK_SIZE = 1024 * 1024; |
2045 | 0 | std::vector<GByte> buffer(CHUNK_SIZE, 0); |
2046 | 0 | while (true) |
2047 | 0 | { |
2048 | 0 | const size_t nRead = |
2049 | 0 | fp->Read(buffer.data(), 1, buffer.size()); |
2050 | 0 | CPLMD5Update(&md5Context, buffer.data(), nRead); |
2051 | 0 | if (nRead < buffer.size()) |
2052 | 0 | { |
2053 | 0 | if (fp->Error()) |
2054 | 0 | { |
2055 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
2056 | 0 | "Error while reading %s", |
2057 | 0 | osFileNameToWrite.c_str()); |
2058 | 0 | return nullptr; |
2059 | 0 | } |
2060 | 0 | break; |
2061 | 0 | } |
2062 | 0 | } |
2063 | 0 | unsigned char digest[16] = {0}; |
2064 | 0 | CPLMD5Final(digest, &md5Context); |
2065 | 0 | char *pszMD5 = CPLBinaryToHex(16, digest); |
2066 | 0 | osId = pszMD5; |
2067 | 0 | CPLFree(pszMD5); |
2068 | 0 | osId += '-'; |
2069 | 0 | osId += CPLGetFilename(osFileNameToWrite.c_str()); |
2070 | 0 | } |
2071 | 0 | else if (psOptions->osIdMethod == "metadata-item") |
2072 | 0 | { |
2073 | 0 | const char *pszId = poSrcDS->GetMetadataItem( |
2074 | 0 | psOptions->osIdMetadataItem.c_str()); |
2075 | 0 | if (!pszId) |
2076 | 0 | { |
2077 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2078 | 0 | "No metadata item '%s' in dataset %s", |
2079 | 0 | psOptions->osIdMetadataItem.c_str(), |
2080 | 0 | osFileNameToWrite.c_str()); |
2081 | 0 | return nullptr; |
2082 | 0 | } |
2083 | 0 | osId = pszId; |
2084 | 0 | } |
2085 | 0 | else if (psOptions->osIdMethod != "filename") |
2086 | 0 | { |
2087 | | // shouldn't happen |
2088 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2089 | 0 | "Unhandled id method '%s'", |
2090 | 0 | psOptions->osIdMethod.c_str()); |
2091 | 0 | return nullptr; |
2092 | 0 | } |
2093 | | |
2094 | 0 | void *ptr = arrayHelper->GetPtrForStringOrBinary( |
2095 | 0 | iIdArray, nBatchSize, osId.size(), false); |
2096 | 0 | if (!ptr) |
2097 | 0 | return nullptr; |
2098 | 0 | memcpy(ptr, osId.data(), osId.size()); |
2099 | 0 | } |
2100 | | |
2101 | | // Write "stac_extensions" |
2102 | 0 | { |
2103 | 0 | uint32_t *panOffsets = static_cast<uint32_t *>( |
2104 | 0 | const_cast<void *>(topArrays[iStacExtensionsArray] |
2105 | 0 | ->buffers[ARROW_BUF_DATA])); |
2106 | 0 | panOffsets[nBatchSize + 1] = |
2107 | 0 | panOffsets[nBatchSize] + COUNT_STAC_EXTENSIONS; |
2108 | |
|
2109 | 0 | { |
2110 | 0 | constexpr const char extension[] = |
2111 | 0 | "https://stac-extensions.github.io/projection/v2.0.0/" |
2112 | 0 | "schema.json"; |
2113 | 0 | constexpr size_t nStrLen = sizeof(extension) - 1; |
2114 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2115 | 0 | stacExtensionSubArray, |
2116 | 0 | COUNT_STAC_EXTENSIONS * nBatchSize + 0, nStrLen, |
2117 | 0 | nStacExtensionSubArrayMaxAlloc, false); |
2118 | 0 | if (!ptr) |
2119 | 0 | return nullptr; |
2120 | 0 | memcpy(ptr, extension, nStrLen); |
2121 | 0 | stacExtensionSubArray->length++; |
2122 | 0 | } |
2123 | | |
2124 | 0 | { |
2125 | 0 | constexpr const char extension[] = |
2126 | 0 | "https://stac-extensions.github.io/eo/v2.0.0/" |
2127 | 0 | "schema.json"; |
2128 | 0 | constexpr size_t nStrLen = sizeof(extension) - 1; |
2129 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2130 | 0 | stacExtensionSubArray, |
2131 | 0 | COUNT_STAC_EXTENSIONS * nBatchSize + 1, nStrLen, |
2132 | 0 | nStacExtensionSubArrayMaxAlloc, false); |
2133 | 0 | if (!ptr) |
2134 | 0 | return nullptr; |
2135 | 0 | memcpy(ptr, extension, nStrLen); |
2136 | 0 | stacExtensionSubArray->length++; |
2137 | 0 | } |
2138 | 0 | } |
2139 | | |
2140 | | // Write "assets.image.href" |
2141 | 0 | { |
2142 | 0 | std::string osHref = osFileNameToWrite; |
2143 | 0 | CPL_IGNORE_RET_VAL(osFileNameToWrite); |
2144 | 0 | if (!psOptions->osBaseURL.empty()) |
2145 | 0 | { |
2146 | 0 | osHref = CPLFormFilenameSafe(psOptions->osBaseURL.c_str(), |
2147 | 0 | CPLGetFilename(osHref.c_str()), |
2148 | 0 | nullptr); |
2149 | 0 | } |
2150 | 0 | else if (VSIIsLocal(osHref.c_str())) |
2151 | 0 | { |
2152 | 0 | if (!CPLIsFilenameRelative(osHref.c_str())) |
2153 | 0 | { |
2154 | 0 | osHref = "file://" + osHref; |
2155 | 0 | } |
2156 | 0 | } |
2157 | 0 | else if (STARTS_WITH(osHref.c_str(), "/vsicurl/")) |
2158 | 0 | { |
2159 | 0 | osHref = osHref.substr(strlen("/vsicurl/")); |
2160 | 0 | } |
2161 | 0 | else if (STARTS_WITH(osHref.c_str(), "/vsis3/")) |
2162 | 0 | { |
2163 | 0 | osHref = "s3://" + osHref.substr(strlen("/vsis3/")); |
2164 | 0 | } |
2165 | 0 | else if (STARTS_WITH(osHref.c_str(), "/vsigs/")) |
2166 | 0 | { |
2167 | 0 | osHref = "gcs://" + osHref.substr(strlen("/vsigs/")); |
2168 | 0 | } |
2169 | 0 | else if (STARTS_WITH(osHref.c_str(), "/vsiaz/")) |
2170 | 0 | { |
2171 | 0 | osHref = "azure://" + osHref.substr(strlen("/vsiaz/")); |
2172 | 0 | } |
2173 | 0 | const size_t nHrefLen = osHref.size(); |
2174 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2175 | 0 | imageHrefArray, nBatchSize, nHrefLen, |
2176 | 0 | nImageHrefArrayMaxAlloc, false); |
2177 | 0 | if (!ptr) |
2178 | 0 | return nullptr; |
2179 | 0 | memcpy(ptr, osHref.data(), nHrefLen); |
2180 | 0 | imageHrefArray->length++; |
2181 | 0 | } |
2182 | | |
2183 | | // Write "assets.image.roles" |
2184 | 0 | { |
2185 | 0 | uint32_t *panRolesOffsets = |
2186 | 0 | static_cast<uint32_t *>(const_cast<void *>( |
2187 | 0 | imageRoleArray->buffers[ARROW_BUF_DATA])); |
2188 | 0 | panRolesOffsets[nBatchSize + 1] = |
2189 | 0 | panRolesOffsets[nBatchSize] + 1; |
2190 | |
|
2191 | 0 | constexpr const char ROLE_DATA[] = "data"; |
2192 | 0 | constexpr size_t nStrLen = sizeof(ROLE_DATA) - 1; |
2193 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2194 | 0 | imageRoleItemArray, nBatchSize, nStrLen, |
2195 | 0 | nImageRoleItemArrayMaxAlloc, false); |
2196 | 0 | if (!ptr) |
2197 | 0 | return nullptr; |
2198 | 0 | memcpy(ptr, ROLE_DATA, nStrLen); |
2199 | 0 | imageRoleItemArray->length++; |
2200 | 0 | } |
2201 | | |
2202 | | // Write "assets.image.type" |
2203 | 0 | if (pszDriverName && EQUAL(pszDriverName, "GTiff")) |
2204 | 0 | { |
2205 | 0 | const char *pszLayout = |
2206 | 0 | poSrcDS->GetMetadataItem("LAYOUT", "IMAGE_STRUCTURE"); |
2207 | 0 | if (pszLayout && EQUAL(pszLayout, "COG")) |
2208 | 0 | { |
2209 | 0 | constexpr const char TYPE[] = |
2210 | 0 | "image/tiff; application=geotiff; " |
2211 | 0 | "profile=cloud-optimized"; |
2212 | 0 | constexpr size_t TYPE_SIZE = sizeof(TYPE) - 1; |
2213 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2214 | 0 | imageTypeArray, nBatchSize, TYPE_SIZE, |
2215 | 0 | nImageTypeArrayMaxAlloc, false); |
2216 | 0 | if (!ptr) |
2217 | 0 | return nullptr; |
2218 | 0 | memcpy(ptr, TYPE, TYPE_SIZE); |
2219 | 0 | } |
2220 | 0 | else |
2221 | 0 | { |
2222 | 0 | constexpr const char TYPE[] = |
2223 | 0 | "image/tiff; application=geotiff"; |
2224 | 0 | constexpr size_t TYPE_SIZE = sizeof(TYPE) - 1; |
2225 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2226 | 0 | imageTypeArray, nBatchSize, TYPE_SIZE, |
2227 | 0 | nImageTypeArrayMaxAlloc, false); |
2228 | 0 | if (!ptr) |
2229 | 0 | return nullptr; |
2230 | 0 | memcpy(ptr, TYPE, TYPE_SIZE); |
2231 | 0 | } |
2232 | 0 | } |
2233 | 0 | else if (pszDriverName && EQUAL(pszDriverName, "PNG")) |
2234 | 0 | { |
2235 | 0 | constexpr const char TYPE[] = "image/png"; |
2236 | 0 | constexpr size_t TYPE_SIZE = sizeof(TYPE) - 1; |
2237 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2238 | 0 | imageTypeArray, nBatchSize, TYPE_SIZE, |
2239 | 0 | nImageTypeArrayMaxAlloc, false); |
2240 | 0 | if (!ptr) |
2241 | 0 | return nullptr; |
2242 | 0 | memcpy(ptr, TYPE, TYPE_SIZE); |
2243 | 0 | } |
2244 | 0 | else if (pszDriverName && EQUAL(pszDriverName, "JPEG")) |
2245 | 0 | { |
2246 | 0 | constexpr const char TYPE[] = "image/jpeg"; |
2247 | 0 | constexpr size_t TYPE_SIZE = sizeof(TYPE) - 1; |
2248 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2249 | 0 | imageTypeArray, nBatchSize, TYPE_SIZE, |
2250 | 0 | nImageTypeArrayMaxAlloc, false); |
2251 | 0 | if (!ptr) |
2252 | 0 | return nullptr; |
2253 | 0 | memcpy(ptr, TYPE, TYPE_SIZE); |
2254 | 0 | } |
2255 | 0 | else if (pszDriverName && (EQUAL(pszDriverName, "JP2KAK") || |
2256 | 0 | EQUAL(pszDriverName, "JP2OpenJPEG") || |
2257 | 0 | EQUAL(pszDriverName, "JP2ECW") || |
2258 | 0 | EQUAL(pszDriverName, "JP2MrSID"))) |
2259 | 0 | { |
2260 | 0 | constexpr const char TYPE[] = "image/jp2"; |
2261 | 0 | constexpr size_t TYPE_SIZE = sizeof(TYPE) - 1; |
2262 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2263 | 0 | imageTypeArray, nBatchSize, TYPE_SIZE, |
2264 | 0 | nImageTypeArrayMaxAlloc, false); |
2265 | 0 | if (!ptr) |
2266 | 0 | return nullptr; |
2267 | 0 | memcpy(ptr, TYPE, TYPE_SIZE); |
2268 | 0 | } |
2269 | 0 | else |
2270 | 0 | { |
2271 | 0 | OGRArrowArrayHelper::SetNull(imageTypeArray, nBatchSize, |
2272 | 0 | nMaxBatchSize, false); |
2273 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary(imageTypeArray, |
2274 | 0 | nBatchSize); |
2275 | 0 | } |
2276 | 0 | imageTypeArray->length++; |
2277 | | |
2278 | | // Write "assets.image.title" |
2279 | 0 | { |
2280 | 0 | OGRArrowArrayHelper::SetNull(imageTitleArray, nBatchSize, |
2281 | 0 | nMaxBatchSize, false); |
2282 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary(imageTitleArray, |
2283 | 0 | nBatchSize); |
2284 | 0 | imageTitleArray->length++; |
2285 | 0 | } |
2286 | | |
2287 | | // Write "bands" |
2288 | 0 | { |
2289 | 0 | const int nThisBands = poSrcDS->GetRasterCount(); |
2290 | 0 | if (nThisBands + nBandsItemCount > nBandsItemAlloc) |
2291 | 0 | { |
2292 | 0 | const auto nOldAlloc = nBandsItemAlloc; |
2293 | 0 | if (nBandsItemAlloc > |
2294 | 0 | std::numeric_limits<uint32_t>::max() / 2) |
2295 | 0 | { |
2296 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Too many bands"); |
2297 | 0 | return nullptr; |
2298 | 0 | } |
2299 | 0 | nBandsItemAlloc = std::max(2 * nBandsItemAlloc, |
2300 | 0 | nThisBands + nBandsItemCount); |
2301 | |
|
2302 | 0 | auto ReallocArray = [nOldAlloc, nBandsItemAlloc]( |
2303 | 0 | ArrowArray *array, size_t nItemSize) |
2304 | 0 | { |
2305 | 0 | if (array->buffers[ARROW_BUF_VALIDITY]) |
2306 | 0 | { |
2307 | | // Bitmap |
2308 | 0 | const uint32_t nNewSizeBytes = |
2309 | 0 | (nBandsItemAlloc + 7) / 8; |
2310 | 0 | char *newPtr = |
2311 | 0 | static_cast<char *>(VSI_REALLOC_VERBOSE( |
2312 | 0 | const_cast<void *>( |
2313 | 0 | array->buffers[ARROW_BUF_VALIDITY]), |
2314 | 0 | nNewSizeBytes)); |
2315 | 0 | if (!newPtr) |
2316 | 0 | return false; |
2317 | 0 | array->buffers[ARROW_BUF_VALIDITY] = |
2318 | 0 | static_cast<const void *>( |
2319 | 0 | const_cast<const char *>(newPtr)); |
2320 | 0 | const uint32_t nOldSizeBytes = (nOldAlloc + 7) / 8; |
2321 | 0 | if (nNewSizeBytes > nOldSizeBytes) |
2322 | 0 | { |
2323 | | // Initialize new allocated bytes as valid |
2324 | | // They are set invalid explicitly with SetNull() |
2325 | 0 | memset(newPtr + nOldSizeBytes, 0xFF, |
2326 | 0 | nNewSizeBytes - nOldSizeBytes); |
2327 | 0 | } |
2328 | 0 | } |
2329 | 0 | char *newPtr = static_cast<char *>(VSI_REALLOC_VERBOSE( |
2330 | 0 | const_cast<void *>(array->buffers[ARROW_BUF_DATA]), |
2331 | 0 | (nBandsItemAlloc + 1) * nItemSize)); |
2332 | 0 | if (!newPtr) |
2333 | 0 | return false; |
2334 | 0 | array->buffers[ARROW_BUF_DATA] = |
2335 | 0 | static_cast<const void *>( |
2336 | 0 | const_cast<const char *>(newPtr)); |
2337 | 0 | memset(newPtr + (nOldAlloc + 1) * nItemSize, 0, |
2338 | 0 | (nBandsItemAlloc - nOldAlloc) * nItemSize); |
2339 | 0 | return true; |
2340 | 0 | }; |
2341 | |
|
2342 | 0 | if (!ReallocArray(bandsNameArray, sizeof(uint32_t)) || |
2343 | 0 | !ReallocArray(bandsCommonNameArray, sizeof(uint32_t)) || |
2344 | 0 | !ReallocArray(bandsCenterWavelengthArray, |
2345 | 0 | sizeof(float)) || |
2346 | 0 | !ReallocArray(bandsFWHMArray, sizeof(float)) || |
2347 | 0 | !ReallocArray(bandsNodataArray, sizeof(uint32_t)) || |
2348 | 0 | !ReallocArray(bandsDataTypeArray, sizeof(uint32_t)) || |
2349 | 0 | !ReallocArray(bandsUnitArray, sizeof(uint32_t))) |
2350 | 0 | { |
2351 | 0 | return nullptr; |
2352 | 0 | } |
2353 | 0 | } |
2354 | | |
2355 | 0 | uint32_t *panBandsOffsets = |
2356 | 0 | static_cast<uint32_t *>(const_cast<void *>( |
2357 | 0 | topArrays[iBandsArray]->buffers[ARROW_BUF_DATA])); |
2358 | 0 | panBandsOffsets[nBatchSize + 1] = |
2359 | 0 | panBandsOffsets[nBatchSize] + nThisBands; |
2360 | |
|
2361 | 0 | for (int i = 0; i < nThisBands; ++i, ++nBandsItemCount) |
2362 | 0 | { |
2363 | 0 | bandsItemArray->length++; |
2364 | |
|
2365 | 0 | const auto poBand = poSrcDS->GetRasterBand(i + 1); |
2366 | 0 | { |
2367 | 0 | std::string osBandName = poBand->GetDescription(); |
2368 | 0 | if (osBandName.empty()) |
2369 | 0 | osBandName = "Band " + std::to_string(i + 1); |
2370 | 0 | void *ptr = |
2371 | 0 | OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2372 | 0 | bandsNameArray, nBandsItemCount, |
2373 | 0 | osBandName.size(), nBandsNameArrayMaxAlloc, |
2374 | 0 | false); |
2375 | 0 | if (!ptr) |
2376 | 0 | return nullptr; |
2377 | 0 | memcpy(ptr, osBandName.data(), osBandName.size()); |
2378 | 0 | bandsNameArray->length++; |
2379 | 0 | } |
2380 | | |
2381 | 0 | const char *pszCommonName = |
2382 | 0 | GDALGetSTACCommonNameFromColorInterp( |
2383 | 0 | poBand->GetColorInterpretation()); |
2384 | 0 | if (pszCommonName) |
2385 | 0 | { |
2386 | 0 | const size_t nLen = strlen(pszCommonName); |
2387 | 0 | void *ptr = |
2388 | 0 | OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2389 | 0 | bandsCommonNameArray, nBandsItemCount, nLen, |
2390 | 0 | nBandsCommonNameArrayMaxAlloc, false); |
2391 | 0 | if (!ptr) |
2392 | 0 | return nullptr; |
2393 | 0 | memcpy(ptr, pszCommonName, nLen); |
2394 | 0 | } |
2395 | 0 | else |
2396 | 0 | { |
2397 | 0 | OGRArrowArrayHelper::SetNull(bandsCommonNameArray, |
2398 | 0 | nBandsItemCount, |
2399 | 0 | nBandsItemAlloc, false); |
2400 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary( |
2401 | 0 | bandsCommonNameArray, nBandsItemCount); |
2402 | 0 | } |
2403 | 0 | bandsCommonNameArray->length++; |
2404 | |
|
2405 | 0 | if (const char *pszCenterWavelength = |
2406 | 0 | poBand->GetMetadataItem("CENTRAL_WAVELENGTH_UM", |
2407 | 0 | "IMAGERY")) |
2408 | 0 | { |
2409 | 0 | float *values = static_cast<float *>( |
2410 | 0 | const_cast<void *>(bandsCenterWavelengthArray |
2411 | 0 | ->buffers[ARROW_BUF_DATA])); |
2412 | 0 | values[nBandsItemCount] = |
2413 | 0 | static_cast<float>(CPLAtof(pszCenterWavelength)); |
2414 | 0 | } |
2415 | 0 | else |
2416 | 0 | { |
2417 | 0 | OGRArrowArrayHelper::SetNull(bandsCenterWavelengthArray, |
2418 | 0 | nBandsItemCount, |
2419 | 0 | nBandsItemAlloc, false); |
2420 | 0 | } |
2421 | 0 | bandsCenterWavelengthArray->length++; |
2422 | |
|
2423 | 0 | if (const char *pszFWHM = |
2424 | 0 | poBand->GetMetadataItem("FWHM_UM", "IMAGERY")) |
2425 | 0 | { |
2426 | 0 | float *values = static_cast<float *>(const_cast<void *>( |
2427 | 0 | bandsFWHMArray->buffers[ARROW_BUF_DATA])); |
2428 | 0 | values[nBandsItemCount] = |
2429 | 0 | static_cast<float>(CPLAtof(pszFWHM)); |
2430 | 0 | } |
2431 | 0 | else |
2432 | 0 | { |
2433 | 0 | OGRArrowArrayHelper::SetNull(bandsFWHMArray, |
2434 | 0 | nBandsItemCount, |
2435 | 0 | nBandsItemAlloc, false); |
2436 | 0 | } |
2437 | 0 | bandsFWHMArray->length++; |
2438 | |
|
2439 | 0 | int bHasNoData = false; |
2440 | 0 | const double dfNoDataValue = |
2441 | 0 | poBand->GetNoDataValue(&bHasNoData); |
2442 | 0 | if (bHasNoData) |
2443 | 0 | { |
2444 | 0 | const std::string osNodata = |
2445 | 0 | std::isnan(dfNoDataValue) ? "nan" |
2446 | 0 | : std::isinf(dfNoDataValue) |
2447 | 0 | ? (dfNoDataValue > 0 ? "inf" : "-inf") |
2448 | 0 | : CPLSPrintf("%.17g", dfNoDataValue); |
2449 | 0 | void *ptr = |
2450 | 0 | OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2451 | 0 | bandsNodataArray, nBandsItemCount, |
2452 | 0 | osNodata.size(), nBandsNodataArrayMaxAlloc, |
2453 | 0 | false); |
2454 | 0 | if (!ptr) |
2455 | 0 | return nullptr; |
2456 | 0 | memcpy(ptr, osNodata.data(), osNodata.size()); |
2457 | 0 | } |
2458 | 0 | else |
2459 | 0 | { |
2460 | 0 | OGRArrowArrayHelper::SetNull(bandsNodataArray, |
2461 | 0 | nBandsItemCount, |
2462 | 0 | nBandsItemAlloc, false); |
2463 | 0 | } |
2464 | 0 | bandsNodataArray->length++; |
2465 | |
|
2466 | 0 | { |
2467 | 0 | const char *pszDT = "other"; |
2468 | | // clang-format off |
2469 | 0 | switch (poBand->GetRasterDataType()) |
2470 | 0 | { |
2471 | 0 | case GDT_Int8: pszDT = "int8"; break; |
2472 | 0 | case GDT_UInt8: pszDT = "uint8"; break; |
2473 | 0 | case GDT_Int16: pszDT = "int16"; break; |
2474 | 0 | case GDT_UInt16: pszDT = "uint16"; break; |
2475 | 0 | case GDT_Int32: pszDT = "int32"; break; |
2476 | 0 | case GDT_UInt32: pszDT = "uint32"; break; |
2477 | 0 | case GDT_Int64: pszDT = "int64"; break; |
2478 | 0 | case GDT_UInt64: pszDT = "uint64"; break; |
2479 | 0 | case GDT_Float16: pszDT = "float16"; break; |
2480 | 0 | case GDT_Float32: pszDT = "float32"; break; |
2481 | 0 | case GDT_Float64: pszDT = "float64"; break; |
2482 | 0 | case GDT_CInt16: pszDT = "cint16"; break; |
2483 | 0 | case GDT_CInt32: pszDT = "cint32"; break; |
2484 | 0 | case GDT_CFloat16: pszDT = "cfloat16"; break; |
2485 | 0 | case GDT_CFloat32: pszDT = "cfloat32"; break; |
2486 | 0 | case GDT_CFloat64: pszDT = "cfloat64"; break; |
2487 | 0 | case GDT_Unknown: break; |
2488 | 0 | case GDT_TypeCount: break; |
2489 | 0 | } |
2490 | | // clang-format on |
2491 | 0 | const size_t nLen = strlen(pszDT); |
2492 | 0 | void *ptr = |
2493 | 0 | OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2494 | 0 | bandsDataTypeArray, nBandsItemCount, nLen, |
2495 | 0 | nBandsDataTypeArrayMaxAlloc, false); |
2496 | 0 | if (!ptr) |
2497 | 0 | return nullptr; |
2498 | 0 | memcpy(ptr, pszDT, nLen); |
2499 | |
|
2500 | 0 | bandsDataTypeArray->length++; |
2501 | 0 | } |
2502 | | |
2503 | 0 | const char *pszUnits = poBand->GetUnitType(); |
2504 | 0 | if (pszUnits && pszUnits[0]) |
2505 | 0 | { |
2506 | 0 | const size_t nLen = strlen(pszUnits); |
2507 | 0 | void *ptr = |
2508 | 0 | OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2509 | 0 | bandsUnitArray, nBandsItemCount, nLen, |
2510 | 0 | nBandsUnitArrayMaxAlloc, false); |
2511 | 0 | if (!ptr) |
2512 | 0 | return nullptr; |
2513 | 0 | memcpy(ptr, pszUnits, nLen); |
2514 | 0 | } |
2515 | 0 | else |
2516 | 0 | { |
2517 | 0 | OGRArrowArrayHelper::SetNull(bandsUnitArray, |
2518 | 0 | nBandsItemCount, |
2519 | 0 | nBandsItemAlloc, false); |
2520 | 0 | } |
2521 | 0 | bandsUnitArray->length++; |
2522 | 0 | } |
2523 | 0 | } |
2524 | | |
2525 | | // Write "proj:code" |
2526 | 0 | bool bHasProjCode = false; |
2527 | 0 | { |
2528 | 0 | auto psArray = topArrays[iProjCode]; |
2529 | 0 | const char *pszSRSAuthName = |
2530 | 0 | poSrcSRS ? poSrcSRS->GetAuthorityName(nullptr) : nullptr; |
2531 | 0 | const char *pszSRSAuthCode = |
2532 | 0 | poSrcSRS ? poSrcSRS->GetAuthorityCode(nullptr) : nullptr; |
2533 | 0 | if (pszSRSAuthName && pszSRSAuthCode) |
2534 | 0 | { |
2535 | 0 | std::string osCode(pszSRSAuthName); |
2536 | 0 | osCode += ':'; |
2537 | 0 | osCode += pszSRSAuthCode; |
2538 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2539 | 0 | psArray, nBatchSize, osCode.size(), |
2540 | 0 | nProjCodeArrayMaxAlloc, false); |
2541 | 0 | if (!ptr) |
2542 | 0 | return nullptr; |
2543 | 0 | memcpy(ptr, osCode.data(), osCode.size()); |
2544 | 0 | bHasProjCode = true; |
2545 | 0 | } |
2546 | 0 | else |
2547 | 0 | { |
2548 | 0 | OGRArrowArrayHelper::SetNull(psArray, nBatchSize, |
2549 | 0 | nMaxBatchSize, false); |
2550 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary(psArray, |
2551 | 0 | nBatchSize); |
2552 | 0 | } |
2553 | 0 | } |
2554 | | |
2555 | | // Write "proj:wkt2" |
2556 | 0 | { |
2557 | 0 | auto psArray = topArrays[iProjWKT2]; |
2558 | 0 | std::string osWKT2; |
2559 | 0 | if (poSrcSRS && !bHasProjCode) |
2560 | 0 | { |
2561 | 0 | const char *const apszOptions[] = {"FORMAT=WKT2_2019", |
2562 | 0 | nullptr}; |
2563 | 0 | osWKT2 = poSrcSRS->exportToWkt(apszOptions); |
2564 | 0 | } |
2565 | 0 | if (!osWKT2.empty()) |
2566 | 0 | { |
2567 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2568 | 0 | psArray, nBatchSize, osWKT2.size(), |
2569 | 0 | nProjWKT2ArrayMaxAlloc, false); |
2570 | 0 | if (!ptr) |
2571 | 0 | return nullptr; |
2572 | 0 | memcpy(ptr, osWKT2.data(), osWKT2.size()); |
2573 | 0 | } |
2574 | 0 | else |
2575 | 0 | { |
2576 | 0 | OGRArrowArrayHelper::SetNull(psArray, nBatchSize, |
2577 | 0 | nMaxBatchSize, false); |
2578 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary(psArray, |
2579 | 0 | nBatchSize); |
2580 | 0 | } |
2581 | 0 | } |
2582 | | |
2583 | | // Write "proj:projjson" |
2584 | 0 | { |
2585 | 0 | auto psArray = topArrays[iProjPROJJSON]; |
2586 | 0 | std::string osPROJJSON; |
2587 | 0 | if (poSrcSRS && !bHasProjCode) |
2588 | 0 | { |
2589 | 0 | char *pszPROJJSON = nullptr; |
2590 | 0 | poSrcSRS->exportToPROJJSON(&pszPROJJSON, nullptr); |
2591 | 0 | if (pszPROJJSON) |
2592 | 0 | osPROJJSON = pszPROJJSON; |
2593 | 0 | CPLFree(pszPROJJSON); |
2594 | 0 | } |
2595 | 0 | if (!osPROJJSON.empty()) |
2596 | 0 | { |
2597 | 0 | void *ptr = OGRArrowArrayHelper::GetPtrForStringOrBinary( |
2598 | 0 | psArray, nBatchSize, osPROJJSON.size(), |
2599 | 0 | nProjPROJJSONArrayMaxAlloc, false); |
2600 | 0 | if (!ptr) |
2601 | 0 | return nullptr; |
2602 | 0 | memcpy(ptr, osPROJJSON.data(), osPROJJSON.size()); |
2603 | 0 | } |
2604 | 0 | else |
2605 | 0 | { |
2606 | 0 | OGRArrowArrayHelper::SetNull(psArray, nBatchSize, |
2607 | 0 | nMaxBatchSize, false); |
2608 | 0 | OGRArrowArrayHelper::SetEmptyStringOrBinary(psArray, |
2609 | 0 | nBatchSize); |
2610 | 0 | } |
2611 | 0 | } |
2612 | | |
2613 | | // Write proj:bbox |
2614 | 0 | { |
2615 | 0 | double *values = static_cast<double *>( |
2616 | 0 | const_cast<void *>(projBBOXItems->buffers[ARROW_BUF_DATA])); |
2617 | 0 | auto ptr = values + nBatchSize * NUM_ITEMS_PROJ_BBOX; |
2618 | 0 | ptr[0] = dfMinXBeforeReproj; |
2619 | 0 | ptr[1] = dfMinYBeforeReproj; |
2620 | 0 | ptr[2] = dfMaxXBeforeReproj; |
2621 | 0 | ptr[3] = dfMaxYBeforeReproj; |
2622 | 0 | } |
2623 | | |
2624 | | // Write proj:shape |
2625 | 0 | { |
2626 | 0 | int32_t *values = static_cast<int32_t *>(const_cast<void *>( |
2627 | 0 | projShapeItems->buffers[ARROW_BUF_DATA])); |
2628 | 0 | auto ptr = values + nBatchSize * NUM_ITEMS_PROJ_SHAPE; |
2629 | 0 | ptr[0] = poSrcDS->GetRasterYSize(); |
2630 | 0 | ptr[1] = poSrcDS->GetRasterXSize(); |
2631 | 0 | } |
2632 | | |
2633 | | // Write proj:transform |
2634 | 0 | { |
2635 | 0 | double *values = static_cast<double *>(const_cast<void *>( |
2636 | 0 | projTransformItems->buffers[ARROW_BUF_DATA])); |
2637 | 0 | auto ptr = values + nBatchSize * NUM_ITEMS_PROJ_TRANSFORM; |
2638 | 0 | ptr[0] = gt.xscale; |
2639 | 0 | ptr[1] = gt.xrot; |
2640 | 0 | ptr[2] = gt.xorig; |
2641 | 0 | ptr[3] = gt.yrot; |
2642 | 0 | ptr[4] = gt.yscale; |
2643 | 0 | ptr[5] = gt.yorig; |
2644 | 0 | ptr[6] = 0; |
2645 | 0 | ptr[7] = 0; |
2646 | 0 | ptr[8] = 1; |
2647 | 0 | } |
2648 | | |
2649 | | // Write geometry |
2650 | 0 | { |
2651 | 0 | const size_t nWKBSize = poPoly->WkbSize(); |
2652 | 0 | void *ptr = arrayHelper->GetPtrForStringOrBinary( |
2653 | 0 | iWkbArray, nBatchSize, nWKBSize, false); |
2654 | 0 | if (!ptr) |
2655 | 0 | return nullptr; |
2656 | 0 | OGRwkbExportOptions sExportOptions; |
2657 | 0 | sExportOptions.eWkbVariant = wkbVariantIso; |
2658 | 0 | if (poPoly->exportToWkb(static_cast<unsigned char *>(ptr), |
2659 | 0 | &sExportOptions) != OGRERR_NONE) |
2660 | 0 | return nullptr; |
2661 | 0 | } |
2662 | | |
2663 | 0 | nBatchSize++; |
2664 | 0 | if (nBatchSize == nMaxBatchSize && !FlushArrays()) |
2665 | 0 | { |
2666 | 0 | return nullptr; |
2667 | 0 | } |
2668 | 0 | } |
2669 | 0 | else |
2670 | 0 | { |
2671 | 0 | auto poFeature = std::make_unique<OGRFeature>(poLayerDefn); |
2672 | 0 | poFeature->SetField(ti_field, osFileNameToWrite.c_str()); |
2673 | |
|
2674 | 0 | if (i_SrcSRSName >= 0 && poSrcSRS) |
2675 | 0 | { |
2676 | 0 | const char *pszAuthorityCode = |
2677 | 0 | poSrcSRS->GetAuthorityCode(nullptr); |
2678 | 0 | const char *pszAuthorityName = |
2679 | 0 | poSrcSRS->GetAuthorityName(nullptr); |
2680 | 0 | if (psOptions->eSrcSRSFormat == FORMAT_AUTO) |
2681 | 0 | { |
2682 | 0 | if (pszAuthorityName != nullptr && |
2683 | 0 | pszAuthorityCode != nullptr) |
2684 | 0 | { |
2685 | 0 | poFeature->SetField( |
2686 | 0 | i_SrcSRSName, CPLSPrintf("%s:%s", pszAuthorityName, |
2687 | 0 | pszAuthorityCode)); |
2688 | 0 | } |
2689 | 0 | else if (nMaxFieldSize == 0 || |
2690 | 0 | strlen(poSrcDS->GetProjectionRef()) <= |
2691 | 0 | static_cast<size_t>(nMaxFieldSize)) |
2692 | 0 | { |
2693 | 0 | poFeature->SetField(i_SrcSRSName, |
2694 | 0 | poSrcDS->GetProjectionRef()); |
2695 | 0 | } |
2696 | 0 | else |
2697 | 0 | { |
2698 | 0 | char *pszProj4 = nullptr; |
2699 | 0 | if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE) |
2700 | 0 | { |
2701 | 0 | poFeature->SetField(i_SrcSRSName, pszProj4); |
2702 | 0 | } |
2703 | 0 | else |
2704 | 0 | { |
2705 | 0 | poFeature->SetField(i_SrcSRSName, |
2706 | 0 | poSrcDS->GetProjectionRef()); |
2707 | 0 | } |
2708 | 0 | CPLFree(pszProj4); |
2709 | 0 | } |
2710 | 0 | } |
2711 | 0 | else if (psOptions->eSrcSRSFormat == FORMAT_WKT) |
2712 | 0 | { |
2713 | 0 | if (nMaxFieldSize == 0 || |
2714 | 0 | strlen(poSrcDS->GetProjectionRef()) <= |
2715 | 0 | static_cast<size_t>(nMaxFieldSize)) |
2716 | 0 | { |
2717 | 0 | poFeature->SetField(i_SrcSRSName, |
2718 | 0 | poSrcDS->GetProjectionRef()); |
2719 | 0 | } |
2720 | 0 | else |
2721 | 0 | { |
2722 | 0 | CPLError( |
2723 | 0 | CE_Warning, CPLE_AppDefined, |
2724 | 0 | "Cannot write WKT for file %s as it is too long!", |
2725 | 0 | osFileNameToWrite.c_str()); |
2726 | 0 | } |
2727 | 0 | } |
2728 | 0 | else if (psOptions->eSrcSRSFormat == FORMAT_PROJ) |
2729 | 0 | { |
2730 | 0 | char *pszProj4 = nullptr; |
2731 | 0 | if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE) |
2732 | 0 | { |
2733 | 0 | poFeature->SetField(i_SrcSRSName, pszProj4); |
2734 | 0 | } |
2735 | 0 | CPLFree(pszProj4); |
2736 | 0 | } |
2737 | 0 | else if (psOptions->eSrcSRSFormat == FORMAT_EPSG) |
2738 | 0 | { |
2739 | 0 | if (pszAuthorityName != nullptr && |
2740 | 0 | pszAuthorityCode != nullptr) |
2741 | 0 | poFeature->SetField( |
2742 | 0 | i_SrcSRSName, CPLSPrintf("%s:%s", pszAuthorityName, |
2743 | 0 | pszAuthorityCode)); |
2744 | 0 | } |
2745 | 0 | } |
2746 | |
|
2747 | 0 | for (const auto &oFetchMD : psOptions->aoFetchMD) |
2748 | 0 | { |
2749 | 0 | if (EQUAL(oFetchMD.osRasterItemName.c_str(), "{PIXEL_SIZE}")) |
2750 | 0 | { |
2751 | 0 | poFeature->SetField(oFetchMD.osFieldName.c_str(), dfRes); |
2752 | 0 | continue; |
2753 | 0 | } |
2754 | | |
2755 | 0 | const char *pszMD = |
2756 | 0 | poSrcDS->GetMetadataItem(oFetchMD.osRasterItemName.c_str()); |
2757 | 0 | if (pszMD) |
2758 | 0 | { |
2759 | 0 | if (EQUAL(oFetchMD.osRasterItemName.c_str(), |
2760 | 0 | "TIFFTAG_DATETIME")) |
2761 | 0 | { |
2762 | 0 | int nYear, nMonth, nDay, nHour, nMin, nSec; |
2763 | 0 | if (sscanf(pszMD, "%04d:%02d:%02d %02d:%02d:%02d", |
2764 | 0 | &nYear, &nMonth, &nDay, &nHour, &nMin, |
2765 | 0 | &nSec) == 6) |
2766 | 0 | { |
2767 | 0 | poFeature->SetField( |
2768 | 0 | oFetchMD.osFieldName.c_str(), |
2769 | 0 | CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", |
2770 | 0 | nYear, nMonth, nDay, nHour, nMin, |
2771 | 0 | nSec)); |
2772 | 0 | continue; |
2773 | 0 | } |
2774 | 0 | } |
2775 | 0 | poFeature->SetField(oFetchMD.osFieldName.c_str(), pszMD); |
2776 | 0 | } |
2777 | 0 | } |
2778 | |
|
2779 | 0 | poFeature->SetGeometryDirectly(poPoly.release()); |
2780 | |
|
2781 | 0 | if (poLayer->CreateFeature(poFeature.get()) != OGRERR_NONE) |
2782 | 0 | { |
2783 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2784 | 0 | "Failed to create feature in tile index."); |
2785 | 0 | return nullptr; |
2786 | 0 | } |
2787 | 0 | } |
2788 | | |
2789 | 0 | ++iCur; |
2790 | 0 | if (psOptions->pfnProgress && |
2791 | 0 | !psOptions->pfnProgress(static_cast<double>(iCur) / nTotal, "", |
2792 | 0 | psOptions->pProgressData)) |
2793 | 0 | { |
2794 | 0 | return nullptr; |
2795 | 0 | } |
2796 | 0 | if (iCur >= nSrcCount) |
2797 | 0 | ++nTotal; |
2798 | 0 | } |
2799 | 0 | if (psOptions->pfnProgress) |
2800 | 0 | psOptions->pfnProgress(1.0, "", psOptions->pProgressData); |
2801 | |
|
2802 | 0 | if (bIsSTACGeoParquet && nBatchSize != 0 && !FlushArrays()) |
2803 | 0 | { |
2804 | 0 | return nullptr; |
2805 | 0 | } |
2806 | | |
2807 | 0 | if (poTileIndexDSUnique) |
2808 | 0 | return GDALDataset::ToHandle(poTileIndexDSUnique.release()); |
2809 | 0 | else |
2810 | 0 | return GDALDataset::ToHandle(poTileIndexDS); |
2811 | 0 | } |
2812 | | |
2813 | | /************************************************************************/ |
2814 | | /* SanitizeSRS */ |
2815 | | /************************************************************************/ |
2816 | | |
2817 | | static char *SanitizeSRS(const char *pszUserInput) |
2818 | | |
2819 | 0 | { |
2820 | 0 | OGRSpatialReferenceH hSRS; |
2821 | 0 | char *pszResult = nullptr; |
2822 | |
|
2823 | 0 | CPLErrorReset(); |
2824 | |
|
2825 | 0 | hSRS = OSRNewSpatialReference(nullptr); |
2826 | 0 | if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE) |
2827 | 0 | OSRExportToWkt(hSRS, &pszResult); |
2828 | 0 | else |
2829 | 0 | { |
2830 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s", |
2831 | 0 | pszUserInput); |
2832 | 0 | } |
2833 | |
|
2834 | 0 | OSRDestroySpatialReference(hSRS); |
2835 | |
|
2836 | 0 | return pszResult; |
2837 | 0 | } |
2838 | | |
2839 | | /************************************************************************/ |
2840 | | /* GDALTileIndexOptionsNew() */ |
2841 | | /************************************************************************/ |
2842 | | |
2843 | | /** |
2844 | | * Allocates a GDALTileIndexOptions struct. |
2845 | | * |
2846 | | * @param papszArgv NULL terminated list of options (potentially including |
2847 | | * filename and open options too), or NULL. The accepted options are the ones of |
2848 | | * the <a href="/programs/gdaltindex.html">gdaltindex</a> utility. |
2849 | | * @param psOptionsForBinary (output) may be NULL (and should generally be |
2850 | | * NULL), otherwise (gdaltindex_bin.cpp use case) must be allocated with |
2851 | | * GDALTileIndexOptionsForBinaryNew() prior to this function. Will be filled |
2852 | | * with potentially present filename, open options,... |
2853 | | * @return pointer to the allocated GDALTileIndexOptions struct. Must be freed |
2854 | | * with GDALTileIndexOptionsFree(). |
2855 | | * |
2856 | | * @since GDAL 3.9 |
2857 | | */ |
2858 | | |
2859 | | GDALTileIndexOptions * |
2860 | | GDALTileIndexOptionsNew(char **papszArgv, |
2861 | | GDALTileIndexOptionsForBinary *psOptionsForBinary) |
2862 | 0 | { |
2863 | 0 | auto psOptions = std::make_unique<GDALTileIndexOptions>(); |
2864 | | |
2865 | | /* -------------------------------------------------------------------- */ |
2866 | | /* Parse arguments. */ |
2867 | | /* -------------------------------------------------------------------- */ |
2868 | |
|
2869 | 0 | CPLStringList aosArgv; |
2870 | |
|
2871 | 0 | if (papszArgv) |
2872 | 0 | { |
2873 | 0 | const int nArgc = CSLCount(papszArgv); |
2874 | 0 | for (int i = 0; i < nArgc; i++) |
2875 | 0 | { |
2876 | 0 | aosArgv.AddString(papszArgv[i]); |
2877 | 0 | } |
2878 | 0 | } |
2879 | |
|
2880 | 0 | try |
2881 | 0 | { |
2882 | 0 | auto argParser = GDALTileIndexAppOptionsGetParser(psOptions.get(), |
2883 | 0 | psOptionsForBinary); |
2884 | 0 | argParser->parse_args_without_binary_name(aosArgv.List()); |
2885 | | |
2886 | | // Check all no store_into args |
2887 | 0 | if (auto oTr = argParser->present<std::vector<double>>("-tr")) |
2888 | 0 | { |
2889 | 0 | psOptions->xres = (*oTr)[0]; |
2890 | 0 | psOptions->yres = (*oTr)[1]; |
2891 | 0 | } |
2892 | |
|
2893 | 0 | if (auto oTargetExtent = argParser->present<std::vector<double>>("-te")) |
2894 | 0 | { |
2895 | 0 | psOptions->xmin = (*oTargetExtent)[0]; |
2896 | 0 | psOptions->ymin = (*oTargetExtent)[1]; |
2897 | 0 | psOptions->xmax = (*oTargetExtent)[2]; |
2898 | 0 | psOptions->ymax = (*oTargetExtent)[3]; |
2899 | 0 | } |
2900 | |
|
2901 | 0 | if (auto fetchMd = |
2902 | 0 | argParser->present<std::vector<std::string>>("-fetch_md")) |
2903 | 0 | { |
2904 | |
|
2905 | 0 | CPLAssert(fetchMd->size() % 3 == 0); |
2906 | | |
2907 | | // Loop |
2908 | 0 | for (size_t i = 0; i < fetchMd->size(); i += 3) |
2909 | 0 | { |
2910 | 0 | OGRFieldType type; |
2911 | 0 | const auto &typeName{fetchMd->at(i + 2)}; |
2912 | 0 | if (typeName == "String") |
2913 | 0 | { |
2914 | 0 | type = OFTString; |
2915 | 0 | } |
2916 | 0 | else if (typeName == "Integer") |
2917 | 0 | { |
2918 | 0 | type = OFTInteger; |
2919 | 0 | } |
2920 | 0 | else if (typeName == "Integer64") |
2921 | 0 | { |
2922 | 0 | type = OFTInteger64; |
2923 | 0 | } |
2924 | 0 | else if (typeName == "Real") |
2925 | 0 | { |
2926 | 0 | type = OFTReal; |
2927 | 0 | } |
2928 | 0 | else if (typeName == "Date") |
2929 | 0 | { |
2930 | 0 | type = OFTDate; |
2931 | 0 | } |
2932 | 0 | else if (typeName == "DateTime") |
2933 | 0 | { |
2934 | 0 | type = OFTDateTime; |
2935 | 0 | } |
2936 | 0 | else |
2937 | 0 | { |
2938 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2939 | 0 | "-fetch_md requires a valid type name as third " |
2940 | 0 | "argument: %s was given.", |
2941 | 0 | fetchMd->at(i).c_str()); |
2942 | 0 | return nullptr; |
2943 | 0 | } |
2944 | | |
2945 | 0 | const GDALTileIndexRasterMetadata oMD{type, fetchMd->at(i + 1), |
2946 | 0 | fetchMd->at(i)}; |
2947 | 0 | psOptions->aoFetchMD.push_back(std::move(oMD)); |
2948 | 0 | } |
2949 | 0 | } |
2950 | | |
2951 | | // Check -t_srs |
2952 | 0 | if (!psOptions->osTargetSRS.empty()) |
2953 | 0 | { |
2954 | 0 | auto sanitized{SanitizeSRS(psOptions->osTargetSRS.c_str())}; |
2955 | 0 | if (sanitized) |
2956 | 0 | { |
2957 | 0 | psOptions->osTargetSRS = sanitized; |
2958 | 0 | CPLFree(sanitized); |
2959 | 0 | } |
2960 | 0 | else |
2961 | 0 | { |
2962 | | // Error was already reported by SanitizeSRS, just return nullptr |
2963 | 0 | psOptions->osTargetSRS.clear(); |
2964 | 0 | return nullptr; |
2965 | 0 | } |
2966 | 0 | } |
2967 | 0 | } |
2968 | 0 | catch (const std::exception &error) |
2969 | 0 | { |
2970 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what()); |
2971 | 0 | return nullptr; |
2972 | 0 | } |
2973 | | |
2974 | 0 | return psOptions.release(); |
2975 | 0 | } |
2976 | | |
2977 | | /************************************************************************/ |
2978 | | /* GDALTileIndexOptionsFree() */ |
2979 | | /************************************************************************/ |
2980 | | |
2981 | | /** |
2982 | | * Frees the GDALTileIndexOptions struct. |
2983 | | * |
2984 | | * @param psOptions the options struct for GDALTileIndex(). |
2985 | | * |
2986 | | * @since GDAL 3.9 |
2987 | | */ |
2988 | | |
2989 | | void GDALTileIndexOptionsFree(GDALTileIndexOptions *psOptions) |
2990 | 0 | { |
2991 | 0 | delete psOptions; |
2992 | 0 | } |
2993 | | |
2994 | | /************************************************************************/ |
2995 | | /* GDALTileIndexOptionsSetProgress() */ |
2996 | | /************************************************************************/ |
2997 | | |
2998 | | /** |
2999 | | * Set a progress function. |
3000 | | * |
3001 | | * @param psOptions the options struct for GDALTileIndex(). |
3002 | | * @param pfnProgress the progress callback. |
3003 | | * @param pProgressData the user data for the progress callback. |
3004 | | * |
3005 | | * @since GDAL 3.11 |
3006 | | */ |
3007 | | |
3008 | | void GDALTileIndexOptionsSetProgress(GDALTileIndexOptions *psOptions, |
3009 | | GDALProgressFunc pfnProgress, |
3010 | | void *pProgressData) |
3011 | 0 | { |
3012 | 0 | psOptions->pfnProgress = pfnProgress; |
3013 | 0 | psOptions->pProgressData = pProgressData; |
3014 | 0 | } |
3015 | | |
3016 | | #undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS |