/src/gdal/apps/gdalmdimtranslate_lib.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL Utilities |
4 | | * Purpose: Command line application to convert a multidimensional raster |
5 | | * Author: Even Rouault,<even.rouault at spatialys.com> |
6 | | * |
7 | | * **************************************************************************** |
8 | | * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_port.h" |
14 | | #include "commonutils.h" |
15 | | #include "gdal_priv.h" |
16 | | #include "gdal_utils.h" |
17 | | #include "gdal_utils_priv.h" |
18 | | #include "gdalargumentparser.h" |
19 | | #include "vrtdataset.h" |
20 | | #include <algorithm> |
21 | | #include <map> |
22 | | #include <set> |
23 | | |
24 | | /************************************************************************/ |
25 | | /* GDALMultiDimTranslateOptions */ |
26 | | /************************************************************************/ |
27 | | |
28 | | struct GDALMultiDimTranslateOptions |
29 | | { |
30 | | std::string osFormat{}; |
31 | | CPLStringList aosCreateOptions{}; |
32 | | std::vector<std::string> aosArraySpec{}; |
33 | | CPLStringList aosArrayOptions{}; |
34 | | std::vector<std::string> aosSubset{}; |
35 | | std::vector<std::string> aosScaleFactor{}; |
36 | | std::vector<std::string> aosGroup{}; |
37 | | GDALProgressFunc pfnProgress = GDALDummyProgress; |
38 | | bool bStrict = false; |
39 | | void *pProgressData = nullptr; |
40 | | bool bUpdate = false; |
41 | | bool bOverwrite = false; |
42 | | bool bNoOverwrite = false; |
43 | | }; |
44 | | |
45 | | /************************************************************************/ |
46 | | /* GDALMultiDimTranslateAppOptionsGetParser() */ |
47 | | /************************************************************************/ |
48 | | |
49 | | static std::unique_ptr<GDALArgumentParser> |
50 | | GDALMultiDimTranslateAppOptionsGetParser( |
51 | | GDALMultiDimTranslateOptions *psOptions, |
52 | | GDALMultiDimTranslateOptionsForBinary *psOptionsForBinary) |
53 | 0 | { |
54 | 0 | auto argParser = std::make_unique<GDALArgumentParser>( |
55 | 0 | "gdalmdimtranslate", /* bForBinary=*/psOptionsForBinary != nullptr); |
56 | |
|
57 | 0 | argParser->add_description( |
58 | 0 | _("Converts multidimensional data between different formats, and " |
59 | 0 | "performs subsetting.")); |
60 | |
|
61 | 0 | argParser->add_epilog( |
62 | 0 | _("For more details, consult " |
63 | 0 | "https://gdal.org/programs/gdalmdimtranslate.html")); |
64 | |
|
65 | 0 | if (psOptionsForBinary) |
66 | 0 | { |
67 | 0 | argParser->add_input_format_argument( |
68 | 0 | &psOptionsForBinary->aosAllowInputDrivers); |
69 | 0 | } |
70 | |
|
71 | 0 | argParser->add_output_format_argument(psOptions->osFormat); |
72 | |
|
73 | 0 | argParser->add_creation_options_argument(psOptions->aosCreateOptions); |
74 | |
|
75 | 0 | auto &group = argParser->add_mutually_exclusive_group(); |
76 | 0 | group.add_argument("-array") |
77 | 0 | .metavar("<array_spec>") |
78 | 0 | .append() |
79 | 0 | .store_into(psOptions->aosArraySpec) |
80 | 0 | .help(_( |
81 | 0 | "Select a single array instead of converting the whole dataset.")); |
82 | |
|
83 | 0 | argParser->add_argument("-arrayoption") |
84 | 0 | .metavar("<NAME>=<VALUE>") |
85 | 0 | .append() |
86 | 0 | .action([psOptions](const std::string &s) |
87 | 0 | { psOptions->aosArrayOptions.AddString(s.c_str()); }) |
88 | 0 | .help(_("Option passed to GDALGroup::GetMDArrayNames() to filter " |
89 | 0 | "arrays.")); |
90 | |
|
91 | 0 | group.add_argument("-group") |
92 | 0 | .metavar("<group_spec>") |
93 | 0 | .append() |
94 | 0 | .store_into(psOptions->aosGroup) |
95 | 0 | .help(_( |
96 | 0 | "Select a single group instead of converting the whole dataset.")); |
97 | | |
98 | | // Note: this is mutually exclusive with "view" option in -array |
99 | 0 | argParser->add_argument("-subset") |
100 | 0 | .metavar("<subset_spec>") |
101 | 0 | .append() |
102 | 0 | .store_into(psOptions->aosSubset) |
103 | 0 | .help(_("Select a subset of the data.")); |
104 | | |
105 | | // Note: this is mutually exclusive with "view" option in -array |
106 | 0 | argParser->add_argument("-scaleaxes") |
107 | 0 | .metavar("<scaleaxes_spec>") |
108 | 0 | .action( |
109 | 0 | [psOptions](const std::string &s) |
110 | 0 | { |
111 | 0 | CPLStringList aosScaleFactors( |
112 | 0 | CSLTokenizeString2(s.c_str(), ",", 0)); |
113 | 0 | for (int j = 0; j < aosScaleFactors.size(); j++) |
114 | 0 | { |
115 | 0 | psOptions->aosScaleFactor.push_back(aosScaleFactors[j]); |
116 | 0 | } |
117 | 0 | }) |
118 | 0 | .help( |
119 | 0 | _("Applies a integral scale factor to one or several dimensions.")); |
120 | |
|
121 | 0 | argParser->add_argument("-strict") |
122 | 0 | .flag() |
123 | 0 | .store_into(psOptions->bStrict) |
124 | 0 | .help(_("Turn warnings into failures.")); |
125 | | |
126 | | // Undocumented option used by gdal mdim convert |
127 | 0 | argParser->add_argument("--overwrite") |
128 | 0 | .store_into(psOptions->bOverwrite) |
129 | 0 | .hidden(); |
130 | | |
131 | | // Undocumented option used by gdal mdim convert |
132 | 0 | argParser->add_argument("--no-overwrite") |
133 | 0 | .store_into(psOptions->bNoOverwrite) |
134 | 0 | .hidden(); |
135 | |
|
136 | 0 | if (psOptionsForBinary) |
137 | 0 | { |
138 | 0 | argParser->add_open_options_argument( |
139 | 0 | psOptionsForBinary->aosOpenOptions); |
140 | |
|
141 | 0 | argParser->add_argument("src_dataset") |
142 | 0 | .metavar("<src_dataset>") |
143 | 0 | .store_into(psOptionsForBinary->osSource) |
144 | 0 | .help(_("The source dataset name.")); |
145 | |
|
146 | 0 | argParser->add_argument("dst_dataset") |
147 | 0 | .metavar("<dst_dataset>") |
148 | 0 | .store_into(psOptionsForBinary->osDest) |
149 | 0 | .help(_("The destination file name.")); |
150 | |
|
151 | 0 | argParser->add_quiet_argument(&psOptionsForBinary->bQuiet); |
152 | 0 | } |
153 | |
|
154 | 0 | return argParser; |
155 | 0 | } |
156 | | |
157 | | /************************************************************************/ |
158 | | /* GDALMultiDimTranslateAppGetParserUsage() */ |
159 | | /************************************************************************/ |
160 | | |
161 | | std::string GDALMultiDimTranslateAppGetParserUsage() |
162 | 0 | { |
163 | 0 | try |
164 | 0 | { |
165 | 0 | GDALMultiDimTranslateOptions sOptions; |
166 | 0 | GDALMultiDimTranslateOptionsForBinary sOptionsForBinary; |
167 | 0 | auto argParser = GDALMultiDimTranslateAppOptionsGetParser( |
168 | 0 | &sOptions, &sOptionsForBinary); |
169 | 0 | return argParser->usage(); |
170 | 0 | } |
171 | 0 | catch (const std::exception &err) |
172 | 0 | { |
173 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", |
174 | 0 | err.what()); |
175 | 0 | return std::string(); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | /************************************************************************/ |
180 | | /* FindMinMaxIdxNumeric() */ |
181 | | /************************************************************************/ |
182 | | |
183 | | static void FindMinMaxIdxNumeric(const GDALMDArray *var, double *pdfTmp, |
184 | | const size_t nCount, const GUInt64 nStartIdx, |
185 | | const double dfMin, const double dfMax, |
186 | | const bool bSlice, bool &bFoundMinIdx, |
187 | | GUInt64 &nMinIdx, bool &bFoundMaxIdx, |
188 | | GUInt64 &nMaxIdx, bool &bLastWasReversed, |
189 | | bool &bEmpty, const double EPS) |
190 | 0 | { |
191 | 0 | if (nCount >= 2) |
192 | 0 | { |
193 | 0 | bool bReversed = false; |
194 | 0 | if (pdfTmp[0] > pdfTmp[nCount - 1]) |
195 | 0 | { |
196 | 0 | bReversed = true; |
197 | 0 | std::reverse(pdfTmp, pdfTmp + nCount); |
198 | 0 | } |
199 | 0 | if (nStartIdx > 0 && bLastWasReversed != bReversed) |
200 | 0 | { |
201 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
202 | 0 | "Variable %s is non monotonic", var->GetName().c_str()); |
203 | 0 | bEmpty = true; |
204 | 0 | return; |
205 | 0 | } |
206 | 0 | bLastWasReversed = bReversed; |
207 | |
|
208 | 0 | if (!bFoundMinIdx) |
209 | 0 | { |
210 | 0 | if (bReversed && nStartIdx == 0 && dfMin > pdfTmp[nCount - 1]) |
211 | 0 | { |
212 | 0 | bEmpty = true; |
213 | 0 | return; |
214 | 0 | } |
215 | 0 | else if (!bReversed && dfMin < pdfTmp[0] - EPS) |
216 | 0 | { |
217 | 0 | if (bSlice) |
218 | 0 | { |
219 | 0 | bEmpty = true; |
220 | 0 | return; |
221 | 0 | } |
222 | 0 | bFoundMinIdx = true; |
223 | 0 | nMinIdx = nStartIdx; |
224 | 0 | } |
225 | 0 | else if (dfMin >= pdfTmp[0] - EPS && |
226 | 0 | dfMin <= pdfTmp[nCount - 1] + EPS) |
227 | 0 | { |
228 | 0 | for (size_t i = 0; i < nCount; i++) |
229 | 0 | { |
230 | 0 | if (dfMin <= pdfTmp[i] + EPS) |
231 | 0 | { |
232 | 0 | bFoundMinIdx = true; |
233 | 0 | nMinIdx = nStartIdx + (bReversed ? nCount - 1 - i : i); |
234 | 0 | break; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | CPLAssert(bFoundMinIdx); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | if (!bFoundMaxIdx) |
241 | 0 | { |
242 | 0 | if (bReversed && nStartIdx == 0 && dfMax > pdfTmp[nCount - 1]) |
243 | 0 | { |
244 | 0 | if (bSlice) |
245 | 0 | { |
246 | 0 | bEmpty = true; |
247 | 0 | return; |
248 | 0 | } |
249 | 0 | bFoundMaxIdx = true; |
250 | 0 | nMaxIdx = 0; |
251 | 0 | } |
252 | 0 | else if (!bReversed && dfMax < pdfTmp[0] - EPS) |
253 | 0 | { |
254 | 0 | if (nStartIdx == 0) |
255 | 0 | { |
256 | 0 | bEmpty = true; |
257 | 0 | return; |
258 | 0 | } |
259 | 0 | bFoundMaxIdx = true; |
260 | 0 | nMaxIdx = nStartIdx - 1; |
261 | 0 | } |
262 | 0 | else if (dfMax > pdfTmp[0] - EPS && |
263 | 0 | dfMax <= pdfTmp[nCount - 1] + EPS) |
264 | 0 | { |
265 | 0 | for (size_t i = 1; i < nCount; i++) |
266 | 0 | { |
267 | 0 | if (dfMax <= pdfTmp[i] - EPS) |
268 | 0 | { |
269 | 0 | bFoundMaxIdx = true; |
270 | 0 | nMaxIdx = nStartIdx + |
271 | 0 | (bReversed ? nCount - 1 - (i - 1) : i - 1); |
272 | 0 | break; |
273 | 0 | } |
274 | 0 | } |
275 | 0 | if (!bFoundMaxIdx) |
276 | 0 | { |
277 | 0 | bFoundMaxIdx = true; |
278 | 0 | nMaxIdx = nStartIdx + (bReversed ? 0 : nCount - 1); |
279 | 0 | } |
280 | 0 | } |
281 | 0 | } |
282 | 0 | } |
283 | 0 | else |
284 | 0 | { |
285 | 0 | if (!bFoundMinIdx) |
286 | 0 | { |
287 | 0 | if (dfMin <= pdfTmp[0] + EPS) |
288 | 0 | { |
289 | 0 | bFoundMinIdx = true; |
290 | 0 | nMinIdx = nStartIdx; |
291 | 0 | } |
292 | 0 | else if (bLastWasReversed && nStartIdx > 0) |
293 | 0 | { |
294 | 0 | bFoundMinIdx = true; |
295 | 0 | nMinIdx = nStartIdx - 1; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | if (!bFoundMaxIdx) |
299 | 0 | { |
300 | 0 | if (dfMax >= pdfTmp[0] - EPS) |
301 | 0 | { |
302 | 0 | bFoundMaxIdx = true; |
303 | 0 | nMaxIdx = nStartIdx; |
304 | 0 | } |
305 | 0 | else if (!bLastWasReversed && nStartIdx > 0) |
306 | 0 | { |
307 | 0 | bFoundMaxIdx = true; |
308 | 0 | nMaxIdx = nStartIdx - 1; |
309 | 0 | } |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | /************************************************************************/ |
315 | | /* FindMinMaxIdxString() */ |
316 | | /************************************************************************/ |
317 | | |
318 | | static void FindMinMaxIdxString(const GDALMDArray *var, const char **ppszTmp, |
319 | | const size_t nCount, const GUInt64 nStartIdx, |
320 | | const std::string &osMin, |
321 | | const std::string &osMax, const bool bSlice, |
322 | | bool &bFoundMinIdx, GUInt64 &nMinIdx, |
323 | | bool &bFoundMaxIdx, GUInt64 &nMaxIdx, |
324 | | bool &bLastWasReversed, bool &bEmpty) |
325 | 0 | { |
326 | 0 | bool bFoundNull = false; |
327 | 0 | for (size_t i = 0; i < nCount; i++) |
328 | 0 | { |
329 | 0 | if (ppszTmp[i] == nullptr) |
330 | 0 | { |
331 | 0 | bFoundNull = true; |
332 | 0 | break; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | if (bFoundNull) |
336 | 0 | { |
337 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
338 | 0 | "Variable %s contains null strings", var->GetName().c_str()); |
339 | 0 | bEmpty = true; |
340 | 0 | return; |
341 | 0 | } |
342 | 0 | if (nCount >= 2) |
343 | 0 | { |
344 | 0 | bool bReversed = false; |
345 | 0 | if (std::string(ppszTmp[0]) > std::string(ppszTmp[nCount - 1])) |
346 | 0 | { |
347 | 0 | bReversed = true; |
348 | 0 | std::reverse(ppszTmp, ppszTmp + nCount); |
349 | 0 | } |
350 | 0 | if (nStartIdx > 0 && bLastWasReversed != bReversed) |
351 | 0 | { |
352 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
353 | 0 | "Variable %s is non monotonic", var->GetName().c_str()); |
354 | 0 | bEmpty = true; |
355 | 0 | return; |
356 | 0 | } |
357 | 0 | bLastWasReversed = bReversed; |
358 | |
|
359 | 0 | if (!bFoundMinIdx) |
360 | 0 | { |
361 | 0 | if (bReversed && nStartIdx == 0 && |
362 | 0 | osMin > std::string(ppszTmp[nCount - 1])) |
363 | 0 | { |
364 | 0 | bEmpty = true; |
365 | 0 | return; |
366 | 0 | } |
367 | 0 | else if (!bReversed && osMin < std::string(ppszTmp[0])) |
368 | 0 | { |
369 | 0 | if (bSlice) |
370 | 0 | { |
371 | 0 | bEmpty = true; |
372 | 0 | return; |
373 | 0 | } |
374 | 0 | bFoundMinIdx = true; |
375 | 0 | nMinIdx = nStartIdx; |
376 | 0 | } |
377 | 0 | else if (osMin >= std::string(ppszTmp[0]) && |
378 | 0 | osMin <= std::string(ppszTmp[nCount - 1])) |
379 | 0 | { |
380 | 0 | for (size_t i = 0; i < nCount; i++) |
381 | 0 | { |
382 | 0 | if (osMin <= std::string(ppszTmp[i])) |
383 | 0 | { |
384 | 0 | bFoundMinIdx = true; |
385 | 0 | nMinIdx = nStartIdx + (bReversed ? nCount - 1 - i : i); |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | CPLAssert(bFoundMinIdx); |
390 | 0 | } |
391 | 0 | } |
392 | 0 | if (!bFoundMaxIdx) |
393 | 0 | { |
394 | 0 | if (bReversed && nStartIdx == 0 && |
395 | 0 | osMax > std::string(ppszTmp[nCount - 1])) |
396 | 0 | { |
397 | 0 | if (bSlice) |
398 | 0 | { |
399 | 0 | bEmpty = true; |
400 | 0 | return; |
401 | 0 | } |
402 | 0 | bFoundMaxIdx = true; |
403 | 0 | nMaxIdx = 0; |
404 | 0 | } |
405 | 0 | else if (!bReversed && osMax < std::string(ppszTmp[0])) |
406 | 0 | { |
407 | 0 | if (nStartIdx == 0) |
408 | 0 | { |
409 | 0 | bEmpty = true; |
410 | 0 | return; |
411 | 0 | } |
412 | 0 | bFoundMaxIdx = true; |
413 | 0 | nMaxIdx = nStartIdx - 1; |
414 | 0 | } |
415 | 0 | else if (osMax == std::string(ppszTmp[0])) |
416 | 0 | { |
417 | 0 | bFoundMaxIdx = true; |
418 | 0 | nMaxIdx = nStartIdx + (bReversed ? nCount - 1 : 0); |
419 | 0 | } |
420 | 0 | else if (osMax > std::string(ppszTmp[0]) && |
421 | 0 | osMax <= std::string(ppszTmp[nCount - 1])) |
422 | 0 | { |
423 | 0 | for (size_t i = 1; i < nCount; i++) |
424 | 0 | { |
425 | 0 | if (osMax <= std::string(ppszTmp[i])) |
426 | 0 | { |
427 | 0 | bFoundMaxIdx = true; |
428 | 0 | if (osMax == std::string(ppszTmp[i])) |
429 | 0 | nMaxIdx = |
430 | 0 | nStartIdx + (bReversed ? nCount - 1 - i : i); |
431 | 0 | else |
432 | 0 | nMaxIdx = |
433 | 0 | nStartIdx + |
434 | 0 | (bReversed ? nCount - 1 - (i - 1) : i - 1); |
435 | 0 | break; |
436 | 0 | } |
437 | 0 | } |
438 | 0 | CPLAssert(bFoundMaxIdx); |
439 | 0 | } |
440 | 0 | } |
441 | 0 | } |
442 | 0 | else |
443 | 0 | { |
444 | 0 | if (!bFoundMinIdx) |
445 | 0 | { |
446 | 0 | if (osMin <= std::string(ppszTmp[0])) |
447 | 0 | { |
448 | 0 | bFoundMinIdx = true; |
449 | 0 | nMinIdx = nStartIdx; |
450 | 0 | } |
451 | 0 | else if (bLastWasReversed && nStartIdx > 0) |
452 | 0 | { |
453 | 0 | bFoundMinIdx = true; |
454 | 0 | nMinIdx = nStartIdx - 1; |
455 | 0 | } |
456 | 0 | } |
457 | 0 | if (!bFoundMaxIdx) |
458 | 0 | { |
459 | 0 | if (osMax >= std::string(ppszTmp[0])) |
460 | 0 | { |
461 | 0 | bFoundMaxIdx = true; |
462 | 0 | nMaxIdx = nStartIdx; |
463 | 0 | } |
464 | 0 | else if (!bLastWasReversed && nStartIdx > 0) |
465 | 0 | { |
466 | 0 | bFoundMaxIdx = true; |
467 | 0 | nMaxIdx = nStartIdx - 1; |
468 | 0 | } |
469 | 0 | } |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | | /************************************************************************/ |
474 | | /* GetDimensionDesc() */ |
475 | | /************************************************************************/ |
476 | | |
477 | | struct DimensionDesc |
478 | | { |
479 | | GUInt64 nStartIdx = 0; |
480 | | GUInt64 nStep = 1; |
481 | | GUInt64 nSize = 0; |
482 | | GUInt64 nOriSize = 0; |
483 | | bool bSlice = false; |
484 | | }; |
485 | | |
486 | | struct DimensionRemapper |
487 | | { |
488 | | std::map<std::string, DimensionDesc> oMap{}; |
489 | | }; |
490 | | |
491 | | static const DimensionDesc * |
492 | | GetDimensionDesc(DimensionRemapper &oDimRemapper, |
493 | | const GDALMultiDimTranslateOptions *psOptions, |
494 | | const std::shared_ptr<GDALDimension> &poDim) |
495 | 0 | { |
496 | 0 | std::string osKey(poDim->GetFullName()); |
497 | 0 | osKey += |
498 | 0 | CPLSPrintf("_" CPL_FRMT_GUIB, static_cast<GUIntBig>(poDim->GetSize())); |
499 | 0 | auto oIter = oDimRemapper.oMap.find(osKey); |
500 | 0 | if (oIter != oDimRemapper.oMap.end() && |
501 | 0 | oIter->second.nOriSize == poDim->GetSize()) |
502 | 0 | { |
503 | 0 | return &(oIter->second); |
504 | 0 | } |
505 | 0 | DimensionDesc desc; |
506 | 0 | desc.nSize = poDim->GetSize(); |
507 | 0 | desc.nOriSize = desc.nSize; |
508 | |
|
509 | 0 | CPLString osRadix(poDim->GetName()); |
510 | 0 | osRadix += '('; |
511 | 0 | for (const auto &subset : psOptions->aosSubset) |
512 | 0 | { |
513 | 0 | if (STARTS_WITH(subset.c_str(), osRadix.c_str())) |
514 | 0 | { |
515 | 0 | auto var = poDim->GetIndexingVariable(); |
516 | 0 | if (!var || var->GetDimensionCount() != 1 || |
517 | 0 | var->GetDimensions()[0]->GetSize() != poDim->GetSize()) |
518 | 0 | { |
519 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
520 | 0 | "Dimension %s has a subset specification, but lacks " |
521 | 0 | "a single dimension indexing variable", |
522 | 0 | poDim->GetName().c_str()); |
523 | 0 | return nullptr; |
524 | 0 | } |
525 | 0 | if (subset.back() != ')') |
526 | 0 | { |
527 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
528 | 0 | "Missing ')' in subset specification."); |
529 | 0 | return nullptr; |
530 | 0 | } |
531 | 0 | CPLStringList aosTokens(CSLTokenizeString2( |
532 | 0 | subset |
533 | 0 | .substr(osRadix.size(), subset.size() - 1 - osRadix.size()) |
534 | 0 | .c_str(), |
535 | 0 | ",", CSLT_HONOURSTRINGS)); |
536 | 0 | if (aosTokens.size() == 1) |
537 | 0 | { |
538 | 0 | desc.bSlice = true; |
539 | 0 | } |
540 | 0 | if (aosTokens.size() != 1 && aosTokens.size() != 2) |
541 | 0 | { |
542 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
543 | 0 | "Invalid number of values in subset specification."); |
544 | 0 | return nullptr; |
545 | 0 | } |
546 | | |
547 | 0 | const bool bIsNumeric = |
548 | 0 | var->GetDataType().GetClass() == GEDTC_NUMERIC; |
549 | 0 | const GDALExtendedDataType dt( |
550 | 0 | bIsNumeric ? GDALExtendedDataType::Create(GDT_Float64) |
551 | 0 | : GDALExtendedDataType::CreateString()); |
552 | |
|
553 | 0 | double dfMin = 0; |
554 | 0 | double dfMax = 0; |
555 | 0 | std::string osMin; |
556 | 0 | std::string osMax; |
557 | 0 | if (bIsNumeric) |
558 | 0 | { |
559 | 0 | if (CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING || |
560 | 0 | (aosTokens.size() == 2 && |
561 | 0 | CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING)) |
562 | 0 | { |
563 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
564 | 0 | "Non numeric bound in subset specification."); |
565 | 0 | return nullptr; |
566 | 0 | } |
567 | 0 | dfMin = CPLAtof(aosTokens[0]); |
568 | 0 | dfMax = dfMin; |
569 | 0 | if (aosTokens.size() == 2) |
570 | 0 | dfMax = CPLAtof(aosTokens[1]); |
571 | 0 | if (dfMin > dfMax) |
572 | 0 | std::swap(dfMin, dfMax); |
573 | 0 | } |
574 | 0 | else |
575 | 0 | { |
576 | 0 | osMin = aosTokens[0]; |
577 | 0 | osMax = osMin; |
578 | 0 | if (aosTokens.size() == 2) |
579 | 0 | osMax = aosTokens[1]; |
580 | 0 | if (osMin > osMax) |
581 | 0 | std::swap(osMin, osMax); |
582 | 0 | } |
583 | | |
584 | 0 | const size_t nDTSize(dt.GetSize()); |
585 | 0 | const size_t nMaxChunkSize = static_cast<size_t>(std::min( |
586 | 0 | static_cast<GUInt64>(10 * 1000 * 1000), poDim->GetSize())); |
587 | 0 | std::vector<GByte> abyTmp(nDTSize * nMaxChunkSize); |
588 | 0 | double *pdfTmp = reinterpret_cast<double *>(&abyTmp[0]); |
589 | 0 | const char **ppszTmp = reinterpret_cast<const char **>(&abyTmp[0]); |
590 | 0 | GUInt64 nStartIdx = 0; |
591 | 0 | const double EPS = std::max(std::max(1e-10, fabs(dfMin) / 1e10), |
592 | 0 | fabs(dfMax) / 1e10); |
593 | 0 | bool bFoundMinIdx = false; |
594 | 0 | bool bFoundMaxIdx = false; |
595 | 0 | GUInt64 nMinIdx = 0; |
596 | 0 | GUInt64 nMaxIdx = 0; |
597 | 0 | bool bLastWasReversed = false; |
598 | 0 | bool bEmpty = false; |
599 | 0 | while (true) |
600 | 0 | { |
601 | 0 | const size_t nCount = static_cast<size_t>( |
602 | 0 | std::min(static_cast<GUInt64>(nMaxChunkSize), |
603 | 0 | poDim->GetSize() - nStartIdx)); |
604 | 0 | if (nCount == 0) |
605 | 0 | break; |
606 | 0 | const GUInt64 anStartId[] = {nStartIdx}; |
607 | 0 | const size_t anCount[] = {nCount}; |
608 | 0 | if (!var->Read(anStartId, anCount, nullptr, nullptr, dt, |
609 | 0 | &abyTmp[0], nullptr, 0)) |
610 | 0 | { |
611 | 0 | return nullptr; |
612 | 0 | } |
613 | 0 | if (bIsNumeric) |
614 | 0 | { |
615 | 0 | FindMinMaxIdxNumeric( |
616 | 0 | var.get(), pdfTmp, nCount, nStartIdx, dfMin, dfMax, |
617 | 0 | desc.bSlice, bFoundMinIdx, nMinIdx, bFoundMaxIdx, |
618 | 0 | nMaxIdx, bLastWasReversed, bEmpty, EPS); |
619 | 0 | } |
620 | 0 | else |
621 | 0 | { |
622 | 0 | FindMinMaxIdxString(var.get(), ppszTmp, nCount, nStartIdx, |
623 | 0 | osMin, osMax, desc.bSlice, bFoundMinIdx, |
624 | 0 | nMinIdx, bFoundMaxIdx, nMaxIdx, |
625 | 0 | bLastWasReversed, bEmpty); |
626 | 0 | } |
627 | 0 | if (dt.NeedsFreeDynamicMemory()) |
628 | 0 | { |
629 | 0 | for (size_t i = 0; i < nCount; i++) |
630 | 0 | { |
631 | 0 | dt.FreeDynamicMemory(&abyTmp[i * nDTSize]); |
632 | 0 | } |
633 | 0 | } |
634 | 0 | if (bEmpty || (bFoundMinIdx && bFoundMaxIdx) || |
635 | 0 | nCount < nMaxChunkSize) |
636 | 0 | { |
637 | 0 | break; |
638 | 0 | } |
639 | 0 | nStartIdx += nMaxChunkSize; |
640 | 0 | } |
641 | | |
642 | | // cppcheck-suppress knownConditionTrueFalse |
643 | 0 | if (!bLastWasReversed) |
644 | 0 | { |
645 | 0 | if (!bFoundMinIdx) |
646 | 0 | bEmpty = true; |
647 | 0 | else if (!bFoundMaxIdx) |
648 | 0 | nMaxIdx = poDim->GetSize() - 1; |
649 | 0 | else |
650 | 0 | bEmpty = nMaxIdx < nMinIdx; |
651 | 0 | } |
652 | 0 | else |
653 | 0 | { |
654 | 0 | if (!bFoundMaxIdx) |
655 | 0 | bEmpty = true; |
656 | 0 | else if (!bFoundMinIdx) |
657 | 0 | nMinIdx = poDim->GetSize() - 1; |
658 | 0 | else |
659 | 0 | bEmpty = nMinIdx < nMaxIdx; |
660 | 0 | } |
661 | 0 | if (bEmpty) |
662 | 0 | { |
663 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
664 | 0 | "Subset specification results in an empty set"); |
665 | 0 | return nullptr; |
666 | 0 | } |
667 | | |
668 | | // cppcheck-suppress knownConditionTrueFalse |
669 | 0 | if (!bLastWasReversed) |
670 | 0 | { |
671 | 0 | CPLAssert(nMaxIdx >= nMinIdx); |
672 | 0 | desc.nStartIdx = nMinIdx; |
673 | 0 | desc.nSize = nMaxIdx - nMinIdx + 1; |
674 | 0 | } |
675 | 0 | else |
676 | 0 | { |
677 | 0 | CPLAssert(nMaxIdx <= nMinIdx); |
678 | 0 | desc.nStartIdx = nMaxIdx; |
679 | 0 | desc.nSize = nMinIdx - nMaxIdx + 1; |
680 | 0 | } |
681 | | |
682 | 0 | break; |
683 | 0 | } |
684 | 0 | } |
685 | | |
686 | 0 | for (const auto &scaleFactor : psOptions->aosScaleFactor) |
687 | 0 | { |
688 | 0 | if (STARTS_WITH(scaleFactor.c_str(), osRadix.c_str())) |
689 | 0 | { |
690 | 0 | if (scaleFactor.back() != ')') |
691 | 0 | { |
692 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
693 | 0 | "Missing ')' in scalefactor specification."); |
694 | 0 | return nullptr; |
695 | 0 | } |
696 | 0 | std::string osScaleFactor(scaleFactor.substr( |
697 | 0 | osRadix.size(), scaleFactor.size() - 1 - osRadix.size())); |
698 | 0 | int nScaleFactor = atoi(osScaleFactor.c_str()); |
699 | 0 | if (CPLGetValueType(osScaleFactor.c_str()) != CPL_VALUE_INTEGER || |
700 | 0 | nScaleFactor <= 0) |
701 | 0 | { |
702 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
703 | 0 | "Only positive integer scale factor is supported"); |
704 | 0 | return nullptr; |
705 | 0 | } |
706 | 0 | desc.nSize /= nScaleFactor; |
707 | 0 | if (desc.nSize == 0) |
708 | 0 | desc.nSize = 1; |
709 | 0 | desc.nStep *= nScaleFactor; |
710 | 0 | break; |
711 | 0 | } |
712 | 0 | } |
713 | | |
714 | 0 | oDimRemapper.oMap[osKey] = desc; |
715 | 0 | return &oDimRemapper.oMap[osKey]; |
716 | 0 | } |
717 | | |
718 | | /************************************************************************/ |
719 | | /* ParseArraySpec() */ |
720 | | /************************************************************************/ |
721 | | |
722 | | // foo |
723 | | // name=foo,transpose=[1,0],view=[0],dstname=bar,ot=Float32 |
724 | | static bool ParseArraySpec(const std::string &arraySpec, std::string &srcName, |
725 | | std::string &dstName, int &band, |
726 | | std::vector<int> &anTransposedAxis, |
727 | | std::string &viewExpr, |
728 | | GDALExtendedDataType &outputType, bool &bResampled) |
729 | 0 | { |
730 | 0 | if (!STARTS_WITH(arraySpec.c_str(), "name=") && |
731 | 0 | !STARTS_WITH(arraySpec.c_str(), "band=")) |
732 | 0 | { |
733 | 0 | srcName = arraySpec; |
734 | 0 | dstName = arraySpec; |
735 | 0 | auto pos = dstName.rfind('/'); |
736 | 0 | if (pos != std::string::npos) |
737 | 0 | dstName = dstName.substr(pos + 1); |
738 | 0 | return true; |
739 | 0 | } |
740 | | |
741 | 0 | std::vector<std::string> tokens; |
742 | 0 | std::string curToken; |
743 | 0 | bool bInArray = false; |
744 | 0 | for (size_t i = 0; i < arraySpec.size(); ++i) |
745 | 0 | { |
746 | 0 | if (!bInArray && arraySpec[i] == ',') |
747 | 0 | { |
748 | 0 | tokens.emplace_back(std::move(curToken)); |
749 | 0 | curToken = std::string(); |
750 | 0 | } |
751 | 0 | else |
752 | 0 | { |
753 | 0 | if (arraySpec[i] == '[') |
754 | 0 | { |
755 | 0 | bInArray = true; |
756 | 0 | } |
757 | 0 | else if (arraySpec[i] == ']') |
758 | 0 | { |
759 | 0 | bInArray = false; |
760 | 0 | } |
761 | 0 | curToken += arraySpec[i]; |
762 | 0 | } |
763 | 0 | } |
764 | 0 | if (!curToken.empty()) |
765 | 0 | { |
766 | 0 | tokens.emplace_back(std::move(curToken)); |
767 | 0 | } |
768 | 0 | for (const auto &token : tokens) |
769 | 0 | { |
770 | 0 | if (STARTS_WITH(token.c_str(), "name=")) |
771 | 0 | { |
772 | 0 | srcName = token.substr(strlen("name=")); |
773 | 0 | if (dstName.empty()) |
774 | 0 | dstName = srcName; |
775 | 0 | } |
776 | 0 | else if (STARTS_WITH(token.c_str(), "band=")) |
777 | 0 | { |
778 | 0 | band = atoi(token.substr(strlen("band=")).c_str()); |
779 | 0 | if (dstName.empty()) |
780 | 0 | dstName = CPLSPrintf("Band%d", band); |
781 | 0 | } |
782 | 0 | else if (STARTS_WITH(token.c_str(), "dstname=")) |
783 | 0 | { |
784 | 0 | dstName = token.substr(strlen("dstname=")); |
785 | 0 | } |
786 | 0 | else if (STARTS_WITH(token.c_str(), "transpose=")) |
787 | 0 | { |
788 | 0 | auto transposeExpr = token.substr(strlen("transpose=")); |
789 | 0 | if (transposeExpr.size() < 3 || transposeExpr[0] != '[' || |
790 | 0 | transposeExpr.back() != ']') |
791 | 0 | { |
792 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
793 | 0 | "Invalid value for transpose"); |
794 | 0 | return false; |
795 | 0 | } |
796 | 0 | transposeExpr = transposeExpr.substr(1, transposeExpr.size() - 2); |
797 | 0 | CPLStringList aosAxis( |
798 | 0 | CSLTokenizeString2(transposeExpr.c_str(), ",", 0)); |
799 | 0 | for (int i = 0; i < aosAxis.size(); ++i) |
800 | 0 | { |
801 | 0 | int iAxis = atoi(aosAxis[i]); |
802 | | // check for non-integer characters |
803 | 0 | if (iAxis == 0) |
804 | 0 | { |
805 | 0 | if (!EQUAL(aosAxis[i], "0")) |
806 | 0 | { |
807 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
808 | 0 | "Invalid value for axis in transpose: %s", |
809 | 0 | aosAxis[i]); |
810 | 0 | return false; |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | 0 | anTransposedAxis.push_back(iAxis); |
815 | 0 | } |
816 | 0 | } |
817 | 0 | else if (STARTS_WITH(token.c_str(), "view=")) |
818 | 0 | { |
819 | 0 | viewExpr = token.substr(strlen("view=")); |
820 | 0 | } |
821 | 0 | else if (STARTS_WITH(token.c_str(), "ot=")) |
822 | 0 | { |
823 | 0 | auto outputTypeStr = token.substr(strlen("ot=")); |
824 | 0 | if (outputTypeStr == "String") |
825 | 0 | outputType = GDALExtendedDataType::CreateString(); |
826 | 0 | else |
827 | 0 | { |
828 | 0 | auto eDT = GDALGetDataTypeByName(outputTypeStr.c_str()); |
829 | 0 | if (eDT == GDT_Unknown) |
830 | 0 | return false; |
831 | 0 | outputType = GDALExtendedDataType::Create(eDT); |
832 | 0 | } |
833 | 0 | } |
834 | 0 | else if (STARTS_WITH(token.c_str(), "resample=")) |
835 | 0 | { |
836 | 0 | bResampled = CPLTestBool(token.c_str() + strlen("resample=")); |
837 | 0 | } |
838 | 0 | else |
839 | 0 | { |
840 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
841 | 0 | "Unexpected array specification part: %s", token.c_str()); |
842 | 0 | return false; |
843 | 0 | } |
844 | 0 | } |
845 | 0 | return true; |
846 | 0 | } |
847 | | |
848 | | /************************************************************************/ |
849 | | /* TranslateArray() */ |
850 | | /************************************************************************/ |
851 | | |
852 | | static bool TranslateArray( |
853 | | DimensionRemapper &oDimRemapper, |
854 | | const std::shared_ptr<GDALMDArray> &poSrcArrayIn, |
855 | | const std::string &arraySpec, |
856 | | const std::shared_ptr<GDALGroup> &poSrcRootGroup, |
857 | | const std::shared_ptr<GDALGroup> &poSrcGroup, |
858 | | const std::shared_ptr<VRTGroup> &poDstRootGroup, |
859 | | std::shared_ptr<VRTGroup> &poDstGroup, GDALDataset *poSrcDS, |
860 | | std::map<std::string, std::shared_ptr<GDALDimension>> &mapSrcToDstDims, |
861 | | std::map<std::string, std::shared_ptr<GDALDimension>> &mapDstDimFullNames, |
862 | | const GDALMultiDimTranslateOptions *psOptions) |
863 | 0 | { |
864 | 0 | std::string srcArrayName; |
865 | 0 | std::string dstArrayName; |
866 | 0 | int band = -1; |
867 | 0 | std::vector<int> anTransposedAxis; |
868 | 0 | std::string viewExpr; |
869 | 0 | bool bResampled = false; |
870 | 0 | GDALExtendedDataType outputType(GDALExtendedDataType::Create(GDT_Unknown)); |
871 | 0 | if (!ParseArraySpec(arraySpec, srcArrayName, dstArrayName, band, |
872 | 0 | anTransposedAxis, viewExpr, outputType, bResampled)) |
873 | 0 | { |
874 | 0 | return false; |
875 | 0 | } |
876 | | |
877 | 0 | std::shared_ptr<GDALMDArray> srcArray; |
878 | 0 | bool bSrcArrayAccessibleThroughSrcGroup = true; |
879 | 0 | if (poSrcRootGroup && poSrcGroup) |
880 | 0 | { |
881 | 0 | if (!srcArrayName.empty() && srcArrayName[0] == '/') |
882 | 0 | srcArray = poSrcRootGroup->OpenMDArrayFromFullname(srcArrayName); |
883 | 0 | else |
884 | 0 | srcArray = poSrcGroup->OpenMDArray(srcArrayName); |
885 | 0 | if (!srcArray) |
886 | 0 | { |
887 | 0 | if (poSrcArrayIn && poSrcArrayIn->GetFullName() == arraySpec) |
888 | 0 | { |
889 | 0 | bSrcArrayAccessibleThroughSrcGroup = false; |
890 | 0 | srcArray = poSrcArrayIn; |
891 | 0 | } |
892 | 0 | else |
893 | 0 | { |
894 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s", |
895 | 0 | srcArrayName.c_str()); |
896 | 0 | return false; |
897 | 0 | } |
898 | 0 | } |
899 | 0 | } |
900 | 0 | else if (band < 0) |
901 | 0 | { |
902 | 0 | srcArray = poSrcDS->AsMDArray(); |
903 | 0 | } |
904 | 0 | else |
905 | 0 | { |
906 | 0 | auto poBand = poSrcDS->GetRasterBand(band); |
907 | 0 | if (!poBand) |
908 | 0 | return false; |
909 | 0 | srcArray = poBand->AsMDArray(); |
910 | 0 | } |
911 | | |
912 | 0 | auto tmpArray = srcArray; |
913 | |
|
914 | 0 | if (bResampled) |
915 | 0 | { |
916 | 0 | auto newTmpArray = |
917 | 0 | tmpArray->GetResampled(std::vector<std::shared_ptr<GDALDimension>>( |
918 | 0 | tmpArray->GetDimensionCount()), |
919 | 0 | GRIORA_NearestNeighbour, nullptr, nullptr); |
920 | 0 | if (!newTmpArray) |
921 | 0 | return false; |
922 | 0 | tmpArray = std::move(newTmpArray); |
923 | 0 | } |
924 | | |
925 | 0 | if (!anTransposedAxis.empty()) |
926 | 0 | { |
927 | 0 | auto newTmpArray = tmpArray->Transpose(anTransposedAxis); |
928 | 0 | if (!newTmpArray) |
929 | 0 | return false; |
930 | 0 | tmpArray = std::move(newTmpArray); |
931 | 0 | } |
932 | 0 | const auto &srcArrayDims(tmpArray->GetDimensions()); |
933 | 0 | std::map<std::shared_ptr<GDALDimension>, std::shared_ptr<GDALDimension>> |
934 | 0 | oMapSubsetDimToSrcDim; |
935 | |
|
936 | 0 | std::vector<GDALMDArray::ViewSpec> viewSpecs; |
937 | 0 | if (!viewExpr.empty()) |
938 | 0 | { |
939 | 0 | if (!psOptions->aosSubset.empty() || !psOptions->aosScaleFactor.empty()) |
940 | 0 | { |
941 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
942 | 0 | "View specification not supported when used together " |
943 | 0 | "with subset and/or scalefactor options"); |
944 | 0 | return false; |
945 | 0 | } |
946 | 0 | auto newTmpArray = tmpArray->GetView(viewExpr, true, viewSpecs); |
947 | 0 | if (!newTmpArray) |
948 | 0 | return false; |
949 | 0 | tmpArray = std::move(newTmpArray); |
950 | 0 | } |
951 | 0 | else if (!psOptions->aosSubset.empty() || |
952 | 0 | !psOptions->aosScaleFactor.empty()) |
953 | 0 | { |
954 | 0 | bool bHasModifiedDim = false; |
955 | 0 | viewExpr = '['; |
956 | 0 | for (size_t i = 0; i < srcArrayDims.size(); ++i) |
957 | 0 | { |
958 | 0 | const auto &srcDim(srcArrayDims[i]); |
959 | 0 | const auto poDimDesc = |
960 | 0 | GetDimensionDesc(oDimRemapper, psOptions, srcDim); |
961 | 0 | if (poDimDesc == nullptr) |
962 | 0 | return false; |
963 | 0 | if (i > 0) |
964 | 0 | viewExpr += ','; |
965 | 0 | if (!poDimDesc->bSlice && poDimDesc->nStartIdx == 0 && |
966 | 0 | poDimDesc->nStep == 1 && poDimDesc->nSize == srcDim->GetSize()) |
967 | 0 | { |
968 | 0 | viewExpr += ":"; |
969 | 0 | } |
970 | 0 | else |
971 | 0 | { |
972 | 0 | bHasModifiedDim = true; |
973 | 0 | viewExpr += CPLSPrintf( |
974 | 0 | CPL_FRMT_GUIB, static_cast<GUInt64>(poDimDesc->nStartIdx)); |
975 | 0 | if (!poDimDesc->bSlice) |
976 | 0 | { |
977 | 0 | viewExpr += ':'; |
978 | 0 | viewExpr += |
979 | 0 | CPLSPrintf(CPL_FRMT_GUIB, |
980 | 0 | static_cast<GUInt64>(poDimDesc->nStartIdx + |
981 | 0 | poDimDesc->nSize * |
982 | 0 | poDimDesc->nStep)); |
983 | 0 | viewExpr += ':'; |
984 | 0 | viewExpr += CPLSPrintf( |
985 | 0 | CPL_FRMT_GUIB, static_cast<GUInt64>(poDimDesc->nStep)); |
986 | 0 | } |
987 | 0 | } |
988 | 0 | } |
989 | 0 | viewExpr += ']'; |
990 | 0 | if (bHasModifiedDim) |
991 | 0 | { |
992 | 0 | auto tmpArrayNew = tmpArray->GetView(viewExpr, false, viewSpecs); |
993 | 0 | if (!tmpArrayNew) |
994 | 0 | return false; |
995 | 0 | tmpArray = std::move(tmpArrayNew); |
996 | 0 | size_t j = 0; |
997 | 0 | const auto &tmpArrayDims(tmpArray->GetDimensions()); |
998 | 0 | for (size_t i = 0; i < srcArrayDims.size(); ++i) |
999 | 0 | { |
1000 | 0 | const auto &srcDim(srcArrayDims[i]); |
1001 | 0 | const auto poDimDesc = |
1002 | 0 | GetDimensionDesc(oDimRemapper, psOptions, srcDim); |
1003 | 0 | if (poDimDesc == nullptr) |
1004 | 0 | return false; |
1005 | 0 | if (poDimDesc->bSlice) |
1006 | 0 | continue; |
1007 | 0 | CPLAssert(j < tmpArrayDims.size()); |
1008 | 0 | oMapSubsetDimToSrcDim[tmpArrayDims[j]] = srcDim; |
1009 | 0 | j++; |
1010 | 0 | } |
1011 | 0 | } |
1012 | 0 | else |
1013 | 0 | { |
1014 | 0 | viewExpr.clear(); |
1015 | 0 | } |
1016 | 0 | } |
1017 | | |
1018 | 0 | int idxSliceSpec = -1; |
1019 | 0 | for (size_t i = 0; i < viewSpecs.size(); ++i) |
1020 | 0 | { |
1021 | 0 | if (viewSpecs[i].m_osFieldName.empty()) |
1022 | 0 | { |
1023 | 0 | if (idxSliceSpec >= 0) |
1024 | 0 | { |
1025 | 0 | idxSliceSpec = -1; |
1026 | 0 | break; |
1027 | 0 | } |
1028 | 0 | else |
1029 | 0 | { |
1030 | 0 | idxSliceSpec = static_cast<int>(i); |
1031 | 0 | } |
1032 | 0 | } |
1033 | 0 | } |
1034 | | |
1035 | | // Map source dimensions to target dimensions |
1036 | 0 | std::vector<std::shared_ptr<GDALDimension>> dstArrayDims; |
1037 | 0 | const auto &tmpArrayDims(tmpArray->GetDimensions()); |
1038 | 0 | for (size_t i = 0; i < tmpArrayDims.size(); ++i) |
1039 | 0 | { |
1040 | 0 | const auto &srcDim(tmpArrayDims[i]); |
1041 | 0 | std::string srcDimFullName(srcDim->GetFullName()); |
1042 | |
|
1043 | 0 | std::shared_ptr<GDALDimension> dstDim; |
1044 | 0 | { |
1045 | 0 | CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler); |
1046 | 0 | if (!srcDimFullName.empty() && srcDimFullName[0] == '/') |
1047 | 0 | { |
1048 | 0 | dstDim = |
1049 | 0 | poDstRootGroup->OpenDimensionFromFullname(srcDimFullName); |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | if (dstDim) |
1053 | 0 | { |
1054 | 0 | dstArrayDims.emplace_back(dstDim); |
1055 | 0 | continue; |
1056 | 0 | } |
1057 | | |
1058 | 0 | auto oIter = mapSrcToDstDims.find(srcDimFullName); |
1059 | 0 | if (oIter != mapSrcToDstDims.end()) |
1060 | 0 | { |
1061 | 0 | dstArrayDims.emplace_back(oIter->second); |
1062 | 0 | continue; |
1063 | 0 | } |
1064 | 0 | auto oIterRealSrcDim = oMapSubsetDimToSrcDim.find(srcDim); |
1065 | 0 | if (oIterRealSrcDim != oMapSubsetDimToSrcDim.end()) |
1066 | 0 | { |
1067 | 0 | srcDimFullName = oIterRealSrcDim->second->GetFullName(); |
1068 | 0 | oIter = mapSrcToDstDims.find(srcDimFullName); |
1069 | 0 | if (oIter != mapSrcToDstDims.end()) |
1070 | 0 | { |
1071 | 0 | dstArrayDims.emplace_back(oIter->second); |
1072 | 0 | continue; |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | 0 | const auto nDimSize = srcDim->GetSize(); |
1077 | 0 | std::string newDimNameFullName(srcDimFullName); |
1078 | 0 | std::string newDimName(srcDim->GetName()); |
1079 | 0 | int nIncr = 2; |
1080 | 0 | std::string osDstGroupFullName(poDstGroup->GetFullName()); |
1081 | 0 | if (osDstGroupFullName == "/") |
1082 | 0 | osDstGroupFullName.clear(); |
1083 | 0 | auto oIter2 = mapDstDimFullNames.find(osDstGroupFullName + '/' + |
1084 | 0 | srcDim->GetName()); |
1085 | 0 | while (oIter2 != mapDstDimFullNames.end() && |
1086 | 0 | oIter2->second->GetSize() != nDimSize) |
1087 | 0 | { |
1088 | 0 | newDimName = srcDim->GetName() + CPLSPrintf("_%d", nIncr); |
1089 | 0 | newDimNameFullName = osDstGroupFullName + '/' + srcDim->GetName() + |
1090 | 0 | CPLSPrintf("_%d", nIncr); |
1091 | 0 | nIncr++; |
1092 | 0 | oIter2 = mapDstDimFullNames.find(newDimNameFullName); |
1093 | 0 | } |
1094 | 0 | if (oIter2 != mapDstDimFullNames.end() && |
1095 | 0 | oIter2->second->GetSize() == nDimSize) |
1096 | 0 | { |
1097 | 0 | dstArrayDims.emplace_back(oIter2->second); |
1098 | 0 | continue; |
1099 | 0 | } |
1100 | | |
1101 | 0 | dstDim = poDstGroup->CreateDimension(newDimName, srcDim->GetType(), |
1102 | 0 | srcDim->GetDirection(), nDimSize); |
1103 | 0 | if (!dstDim) |
1104 | 0 | return false; |
1105 | 0 | if (!srcDimFullName.empty() && srcDimFullName[0] == '/') |
1106 | 0 | { |
1107 | 0 | mapSrcToDstDims[srcDimFullName] = dstDim; |
1108 | 0 | } |
1109 | 0 | mapDstDimFullNames[dstDim->GetFullName()] = dstDim; |
1110 | 0 | dstArrayDims.emplace_back(dstDim); |
1111 | |
|
1112 | 0 | std::shared_ptr<GDALMDArray> srcIndexVar; |
1113 | 0 | GDALMDArray::Range range; |
1114 | 0 | range.m_nStartIdx = 0; |
1115 | 0 | range.m_nIncr = 1; |
1116 | 0 | std::string indexingVarSpec; |
1117 | 0 | if (idxSliceSpec >= 0) |
1118 | 0 | { |
1119 | 0 | const auto &viewSpec(viewSpecs[idxSliceSpec]); |
1120 | 0 | auto iParentDim = viewSpec.m_mapDimIdxToParentDimIdx[i]; |
1121 | 0 | if (iParentDim != static_cast<size_t>(-1) && |
1122 | 0 | (srcIndexVar = |
1123 | 0 | srcArrayDims[iParentDim]->GetIndexingVariable()) != |
1124 | 0 | nullptr && |
1125 | 0 | srcIndexVar->GetDimensionCount() == 1 && |
1126 | 0 | srcIndexVar->GetFullName() != srcArray->GetFullName()) |
1127 | 0 | { |
1128 | 0 | CPLAssert(iParentDim < viewSpec.m_parentRanges.size()); |
1129 | 0 | range = viewSpec.m_parentRanges[iParentDim]; |
1130 | 0 | indexingVarSpec = "name=" + srcIndexVar->GetFullName(); |
1131 | 0 | indexingVarSpec += ",dstname=" + newDimName; |
1132 | 0 | if (psOptions->aosSubset.empty() && |
1133 | 0 | psOptions->aosScaleFactor.empty()) |
1134 | 0 | { |
1135 | 0 | if (range.m_nStartIdx != 0 || range.m_nIncr != 1 || |
1136 | 0 | srcArrayDims[iParentDim]->GetSize() != |
1137 | 0 | srcDim->GetSize()) |
1138 | 0 | { |
1139 | 0 | indexingVarSpec += ",view=["; |
1140 | 0 | if (range.m_nIncr > 0 || |
1141 | 0 | range.m_nStartIdx != srcDim->GetSize() - 1) |
1142 | 0 | { |
1143 | 0 | indexingVarSpec += |
1144 | 0 | CPLSPrintf(CPL_FRMT_GUIB, range.m_nStartIdx); |
1145 | 0 | } |
1146 | 0 | indexingVarSpec += ':'; |
1147 | 0 | if (range.m_nIncr > 0) |
1148 | 0 | { |
1149 | 0 | const auto nEndIdx = |
1150 | 0 | range.m_nStartIdx + |
1151 | 0 | range.m_nIncr * srcDim->GetSize(); |
1152 | 0 | indexingVarSpec += |
1153 | 0 | CPLSPrintf(CPL_FRMT_GUIB, nEndIdx); |
1154 | 0 | } |
1155 | 0 | else if (range.m_nStartIdx > |
1156 | 0 | -range.m_nIncr * srcDim->GetSize()) |
1157 | 0 | { |
1158 | 0 | const auto nEndIdx = |
1159 | 0 | range.m_nStartIdx + |
1160 | 0 | range.m_nIncr * srcDim->GetSize(); |
1161 | 0 | indexingVarSpec += |
1162 | 0 | CPLSPrintf(CPL_FRMT_GUIB, nEndIdx - 1); |
1163 | 0 | } |
1164 | 0 | indexingVarSpec += ':'; |
1165 | 0 | indexingVarSpec += |
1166 | 0 | CPLSPrintf(CPL_FRMT_GIB, range.m_nIncr); |
1167 | 0 | indexingVarSpec += ']'; |
1168 | 0 | } |
1169 | 0 | } |
1170 | 0 | } |
1171 | 0 | } |
1172 | 0 | else |
1173 | 0 | { |
1174 | 0 | srcIndexVar = srcDim->GetIndexingVariable(); |
1175 | 0 | if (srcIndexVar) |
1176 | 0 | { |
1177 | 0 | indexingVarSpec = srcIndexVar->GetFullName(); |
1178 | 0 | } |
1179 | 0 | } |
1180 | 0 | if (srcIndexVar && !indexingVarSpec.empty() && |
1181 | 0 | srcIndexVar->GetFullName() != srcArray->GetFullName()) |
1182 | 0 | { |
1183 | 0 | if (poSrcRootGroup) |
1184 | 0 | { |
1185 | 0 | if (!TranslateArray(oDimRemapper, srcIndexVar, indexingVarSpec, |
1186 | 0 | poSrcRootGroup, poSrcGroup, poDstRootGroup, |
1187 | 0 | poDstGroup, poSrcDS, mapSrcToDstDims, |
1188 | 0 | mapDstDimFullNames, psOptions)) |
1189 | 0 | { |
1190 | 0 | return false; |
1191 | 0 | } |
1192 | 0 | } |
1193 | 0 | else |
1194 | 0 | { |
1195 | 0 | bool bIndexingVarCreated = false; |
1196 | 0 | if (srcIndexVar->GetName() == "X" || |
1197 | 0 | srcIndexVar->GetName() == "Y") |
1198 | 0 | { |
1199 | 0 | GDALGeoTransform gt; |
1200 | 0 | if (poSrcDS->GetGeoTransform(gt) == CE_None && |
1201 | 0 | gt.IsAxisAligned()) |
1202 | 0 | { |
1203 | 0 | auto var = poDstGroup->CreateVRTMDArray( |
1204 | 0 | newDimName, {dstDim}, |
1205 | 0 | GDALExtendedDataType::Create(GDT_Float64)); |
1206 | 0 | if (var) |
1207 | 0 | { |
1208 | 0 | const double dfStart = |
1209 | 0 | srcIndexVar->GetName() == "X" |
1210 | 0 | ? gt.xorig + |
1211 | 0 | (range.m_nStartIdx + 0.5) * gt.xscale |
1212 | 0 | : gt.yorig + |
1213 | 0 | (range.m_nStartIdx + 0.5) * gt.yscale; |
1214 | 0 | const double dfIncr = |
1215 | 0 | (srcIndexVar->GetName() == "X" ? gt.xscale |
1216 | 0 | : gt.yscale) * |
1217 | 0 | range.m_nIncr; |
1218 | 0 | auto poSource = std::make_unique< |
1219 | 0 | VRTMDArraySourceRegularlySpaced>(dfStart, |
1220 | 0 | dfIncr); |
1221 | 0 | var->AddSource(std::move(poSource)); |
1222 | 0 | bIndexingVarCreated = true; |
1223 | 0 | } |
1224 | | // else: error emitted by CreateVRTMDArray() |
1225 | 0 | } |
1226 | 0 | } |
1227 | | |
1228 | | // Arbitrary: to avoid blowing up RAM |
1229 | 0 | constexpr size_t MAX_SIZE_FOR_INDEXING_VAR = 1000 * 1000; |
1230 | 0 | if (!bIndexingVarCreated && |
1231 | 0 | srcIndexVar->GetDimensionCount() == 1 && |
1232 | 0 | srcIndexVar->GetDataType().GetClass() != GEDTC_COMPOUND && |
1233 | 0 | srcIndexVar->GetDimensions()[0]->GetSize() < |
1234 | 0 | MAX_SIZE_FOR_INDEXING_VAR) |
1235 | 0 | { |
1236 | 0 | auto var = poDstGroup->CreateVRTMDArray( |
1237 | 0 | newDimName, {dstDim}, srcIndexVar->GetDataType()); |
1238 | 0 | if (var) |
1239 | 0 | { |
1240 | 0 | std::vector<GUInt64> anOffset = {0}; |
1241 | 0 | const size_t nCount = static_cast<size_t>( |
1242 | 0 | srcIndexVar->GetDimensions()[0]->GetSize()); |
1243 | 0 | std::vector<size_t> anCount = {nCount}; |
1244 | 0 | std::vector<GByte> abyValues; |
1245 | 0 | abyValues.resize(srcIndexVar->GetDataType().GetSize() * |
1246 | 0 | nCount); |
1247 | 0 | const GInt64 arrayStep[] = {1}; |
1248 | 0 | const GPtrDiff_t anBufferStride[] = {1}; |
1249 | 0 | srcIndexVar->Read(anOffset.data(), anCount.data(), |
1250 | 0 | arrayStep, anBufferStride, |
1251 | 0 | srcIndexVar->GetDataType(), |
1252 | 0 | abyValues.data()); |
1253 | 0 | auto poSource = |
1254 | 0 | std::make_unique<VRTMDArraySourceInlinedValues>( |
1255 | 0 | var.get(), |
1256 | 0 | /* bIsConstantValue = */ false, |
1257 | 0 | std::move(anOffset), std::move(anCount), |
1258 | 0 | std::move(abyValues)); |
1259 | 0 | var->AddSource(std::move(poSource)); |
1260 | 0 | } |
1261 | | // else: error emitted by CreateVRTMDArray() |
1262 | 0 | } |
1263 | 0 | else if (!bIndexingVarCreated) |
1264 | 0 | { |
1265 | 0 | CPLDebug("GDAL", "Cannot create indexing variable for %s", |
1266 | 0 | srcIndexVar->GetName().c_str()); |
1267 | 0 | } |
1268 | 0 | } |
1269 | | |
1270 | 0 | CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler); |
1271 | 0 | auto poDstIndexingVar(poDstGroup->OpenMDArray(newDimName)); |
1272 | 0 | if (poDstIndexingVar) |
1273 | 0 | dstDim->SetIndexingVariable(std::move(poDstIndexingVar)); |
1274 | 0 | } |
1275 | 0 | } |
1276 | 0 | if (outputType.GetClass() == GEDTC_NUMERIC && |
1277 | 0 | outputType.GetNumericDataType() == GDT_Unknown) |
1278 | 0 | { |
1279 | 0 | outputType = GDALExtendedDataType(tmpArray->GetDataType()); |
1280 | 0 | } |
1281 | |
|
1282 | 0 | CPLStringList aosArrayCO; |
1283 | 0 | if (!bResampled && anTransposedAxis.empty() && viewExpr.empty() && |
1284 | 0 | psOptions->aosSubset.empty() && psOptions->aosScaleFactor.empty() && |
1285 | 0 | srcArray->GetDimensionCount() == dstArrayDims.size()) |
1286 | 0 | { |
1287 | 0 | const auto anBlockSize = srcArray->GetBlockSize(); |
1288 | 0 | std::string osBlockSize; |
1289 | 0 | for (auto v : anBlockSize) |
1290 | 0 | { |
1291 | 0 | if (v == 0) |
1292 | 0 | { |
1293 | 0 | osBlockSize.clear(); |
1294 | 0 | break; |
1295 | 0 | } |
1296 | 0 | if (!osBlockSize.empty()) |
1297 | 0 | osBlockSize += ','; |
1298 | 0 | osBlockSize += std::to_string(v); |
1299 | 0 | } |
1300 | 0 | if (!osBlockSize.empty()) |
1301 | 0 | aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str()); |
1302 | 0 | } |
1303 | |
|
1304 | 0 | auto dstArray = poDstGroup->CreateVRTMDArray(dstArrayName, dstArrayDims, |
1305 | 0 | outputType, aosArrayCO.List()); |
1306 | 0 | if (!dstArray) |
1307 | 0 | return false; |
1308 | | |
1309 | 0 | GUInt64 nCurCost = 0; |
1310 | 0 | dstArray->CopyFromAllExceptValues(srcArray.get(), false, nCurCost, 0, |
1311 | 0 | nullptr, nullptr); |
1312 | 0 | if (bResampled) |
1313 | 0 | dstArray->SetSpatialRef(tmpArray->GetSpatialRef().get()); |
1314 | |
|
1315 | 0 | if (idxSliceSpec >= 0) |
1316 | 0 | { |
1317 | 0 | std::set<size_t> oSetParentDimIdxNotInArray; |
1318 | 0 | for (size_t i = 0; i < srcArrayDims.size(); ++i) |
1319 | 0 | { |
1320 | 0 | oSetParentDimIdxNotInArray.insert(i); |
1321 | 0 | } |
1322 | 0 | const auto &viewSpec(viewSpecs[idxSliceSpec]); |
1323 | 0 | for (size_t i = 0; i < tmpArrayDims.size(); ++i) |
1324 | 0 | { |
1325 | 0 | auto iParentDim = viewSpec.m_mapDimIdxToParentDimIdx[i]; |
1326 | 0 | if (iParentDim != static_cast<size_t>(-1)) |
1327 | 0 | { |
1328 | 0 | oSetParentDimIdxNotInArray.erase(iParentDim); |
1329 | 0 | } |
1330 | 0 | } |
1331 | 0 | for (const auto parentDimIdx : oSetParentDimIdxNotInArray) |
1332 | 0 | { |
1333 | 0 | const auto &srcDim(srcArrayDims[parentDimIdx]); |
1334 | 0 | const auto nStartIdx = |
1335 | 0 | viewSpec.m_parentRanges[parentDimIdx].m_nStartIdx; |
1336 | 0 | if (nStartIdx < static_cast<GUInt64>(INT_MAX)) |
1337 | 0 | { |
1338 | 0 | auto dstAttr = dstArray->CreateAttribute( |
1339 | 0 | "DIM_" + srcDim->GetName() + "_INDEX", {}, |
1340 | 0 | GDALExtendedDataType::Create(GDT_Int32)); |
1341 | 0 | dstAttr->Write(static_cast<int>(nStartIdx)); |
1342 | 0 | } |
1343 | 0 | else |
1344 | 0 | { |
1345 | 0 | auto dstAttr = dstArray->CreateAttribute( |
1346 | 0 | "DIM_" + srcDim->GetName() + "_INDEX", {}, |
1347 | 0 | GDALExtendedDataType::CreateString()); |
1348 | 0 | dstAttr->Write(CPLSPrintf(CPL_FRMT_GUIB, |
1349 | 0 | static_cast<GUIntBig>(nStartIdx))); |
1350 | 0 | } |
1351 | |
|
1352 | 0 | auto srcIndexVar(srcDim->GetIndexingVariable()); |
1353 | 0 | if (srcIndexVar && srcIndexVar->GetDimensionCount() == 1) |
1354 | 0 | { |
1355 | 0 | const auto &dt(srcIndexVar->GetDataType()); |
1356 | 0 | std::vector<GByte> abyTmp(dt.GetSize()); |
1357 | 0 | const size_t nCount = 1; |
1358 | 0 | if (srcIndexVar->Read(&nStartIdx, &nCount, nullptr, nullptr, dt, |
1359 | 0 | &abyTmp[0], nullptr, 0)) |
1360 | 0 | { |
1361 | 0 | { |
1362 | 0 | auto dstAttr = dstArray->CreateAttribute( |
1363 | 0 | "DIM_" + srcDim->GetName() + "_VALUE", {}, dt); |
1364 | 0 | dstAttr->Write(abyTmp.data(), abyTmp.size()); |
1365 | 0 | dt.FreeDynamicMemory(&abyTmp[0]); |
1366 | 0 | } |
1367 | |
|
1368 | 0 | const auto &unit(srcIndexVar->GetUnit()); |
1369 | 0 | if (!unit.empty()) |
1370 | 0 | { |
1371 | 0 | auto dstAttr = dstArray->CreateAttribute( |
1372 | 0 | "DIM_" + srcDim->GetName() + "_UNIT", {}, |
1373 | 0 | GDALExtendedDataType::CreateString()); |
1374 | 0 | dstAttr->Write(unit.c_str()); |
1375 | 0 | } |
1376 | 0 | } |
1377 | 0 | } |
1378 | 0 | } |
1379 | 0 | } |
1380 | |
|
1381 | 0 | double dfStart = 0.0; |
1382 | 0 | double dfIncrement = 0.0; |
1383 | 0 | if (!bSrcArrayAccessibleThroughSrcGroup && |
1384 | 0 | tmpArray->IsRegularlySpaced(dfStart, dfIncrement)) |
1385 | 0 | { |
1386 | 0 | auto poSource = std::make_unique<VRTMDArraySourceRegularlySpaced>( |
1387 | 0 | dfStart, dfIncrement); |
1388 | 0 | dstArray->AddSource(std::move(poSource)); |
1389 | 0 | } |
1390 | 0 | else |
1391 | 0 | { |
1392 | 0 | const auto dimCount(tmpArray->GetDimensionCount()); |
1393 | 0 | std::vector<GUInt64> anSrcOffset(dimCount); |
1394 | 0 | std::vector<GUInt64> anCount(dimCount); |
1395 | 0 | for (size_t i = 0; i < dimCount; ++i) |
1396 | 0 | { |
1397 | 0 | anCount[i] = tmpArrayDims[i]->GetSize(); |
1398 | 0 | } |
1399 | 0 | std::vector<GUInt64> anStep(dimCount, 1); |
1400 | 0 | std::vector<GUInt64> anDstOffset(dimCount); |
1401 | 0 | auto poSource = std::make_unique<VRTMDArraySourceFromArray>( |
1402 | 0 | dstArray.get(), false, false, poSrcDS->GetDescription(), |
1403 | 0 | band < 0 ? srcArray->GetFullName() : std::string(), |
1404 | 0 | band >= 1 ? CPLSPrintf("%d", band) : std::string(), |
1405 | 0 | std::move(anTransposedAxis), |
1406 | 0 | bResampled ? (viewExpr.empty() |
1407 | 0 | ? std::string("resample=true") |
1408 | 0 | : std::string("resample=true,").append(viewExpr)) |
1409 | 0 | : std::move(viewExpr), |
1410 | 0 | std::move(anSrcOffset), std::move(anCount), std::move(anStep), |
1411 | 0 | std::move(anDstOffset)); |
1412 | 0 | dstArray->AddSource(std::move(poSource)); |
1413 | 0 | } |
1414 | |
|
1415 | 0 | return true; |
1416 | 0 | } |
1417 | | |
1418 | | /************************************************************************/ |
1419 | | /* GetGroup() */ |
1420 | | /************************************************************************/ |
1421 | | |
1422 | | static std::shared_ptr<GDALGroup> |
1423 | | GetGroup(const std::shared_ptr<GDALGroup> &poRootGroup, |
1424 | | const std::string &fullName) |
1425 | 0 | { |
1426 | 0 | auto poCurGroup = poRootGroup; |
1427 | 0 | CPLStringList aosTokens(CSLTokenizeString2(fullName.c_str(), "/", 0)); |
1428 | 0 | for (int i = 0; i < aosTokens.size(); i++) |
1429 | 0 | { |
1430 | 0 | auto poCurGroupNew = poCurGroup->OpenGroup(aosTokens[i], nullptr); |
1431 | 0 | if (!poCurGroupNew) |
1432 | 0 | { |
1433 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s", |
1434 | 0 | aosTokens[i]); |
1435 | 0 | return nullptr; |
1436 | 0 | } |
1437 | 0 | poCurGroup = std::move(poCurGroupNew); |
1438 | 0 | } |
1439 | 0 | return poCurGroup; |
1440 | 0 | } |
1441 | | |
1442 | | /************************************************************************/ |
1443 | | /* CopyGroup() */ |
1444 | | /************************************************************************/ |
1445 | | |
1446 | | static bool CopyGroup( |
1447 | | DimensionRemapper &oDimRemapper, |
1448 | | const std::shared_ptr<VRTGroup> &poDstRootGroup, |
1449 | | std::shared_ptr<VRTGroup> &poDstGroup, |
1450 | | const std::shared_ptr<GDALGroup> &poSrcRootGroup, |
1451 | | const std::shared_ptr<GDALGroup> &poSrcGroup, GDALDataset *poSrcDS, |
1452 | | std::map<std::string, std::shared_ptr<GDALDimension>> &mapSrcToDstDims, |
1453 | | std::map<std::string, std::shared_ptr<GDALDimension>> &mapDstDimFullNames, |
1454 | | const GDALMultiDimTranslateOptions *psOptions, bool bRecursive) |
1455 | 0 | { |
1456 | 0 | const auto srcDims = poSrcGroup->GetDimensions(); |
1457 | 0 | std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName; |
1458 | 0 | for (const auto &dim : srcDims) |
1459 | 0 | { |
1460 | 0 | const auto poDimDesc = GetDimensionDesc(oDimRemapper, psOptions, dim); |
1461 | 0 | if (poDimDesc == nullptr) |
1462 | 0 | return false; |
1463 | 0 | if (poDimDesc->bSlice) |
1464 | 0 | continue; |
1465 | 0 | auto dstDim = |
1466 | 0 | poDstGroup->CreateDimension(dim->GetName(), dim->GetType(), |
1467 | 0 | dim->GetDirection(), poDimDesc->nSize); |
1468 | 0 | if (!dstDim) |
1469 | 0 | return false; |
1470 | 0 | mapSrcToDstDims[dim->GetFullName()] = dstDim; |
1471 | 0 | mapDstDimFullNames[dstDim->GetFullName()] = dstDim; |
1472 | 0 | auto poIndexingVarSrc(dim->GetIndexingVariable()); |
1473 | 0 | if (poIndexingVarSrc) |
1474 | 0 | { |
1475 | 0 | mapSrcVariableNameToIndexedDimName[poIndexingVarSrc->GetName()] = |
1476 | 0 | dim->GetFullName(); |
1477 | 0 | } |
1478 | 0 | } |
1479 | | |
1480 | 0 | if (!(poSrcGroup == poSrcRootGroup && psOptions->aosGroup.empty())) |
1481 | 0 | { |
1482 | 0 | auto attrs = poSrcGroup->GetAttributes(); |
1483 | 0 | for (const auto &attr : attrs) |
1484 | 0 | { |
1485 | 0 | auto dstAttr = poDstGroup->CreateAttribute( |
1486 | 0 | attr->GetName(), attr->GetDimensionsSize(), |
1487 | 0 | attr->GetDataType()); |
1488 | 0 | if (!dstAttr) |
1489 | 0 | { |
1490 | 0 | if (!psOptions->bStrict) |
1491 | 0 | continue; |
1492 | 0 | return false; |
1493 | 0 | } |
1494 | 0 | auto raw(attr->ReadAsRaw()); |
1495 | 0 | if (!dstAttr->Write(raw.data(), raw.size()) && !psOptions->bStrict) |
1496 | 0 | return false; |
1497 | 0 | } |
1498 | 0 | } |
1499 | | |
1500 | 0 | auto arrayNames = |
1501 | 0 | poSrcGroup->GetMDArrayNames(psOptions->aosArrayOptions.List()); |
1502 | 0 | for (const auto &name : arrayNames) |
1503 | 0 | { |
1504 | 0 | if (!TranslateArray(oDimRemapper, nullptr, name, poSrcRootGroup, |
1505 | 0 | poSrcGroup, poDstRootGroup, poDstGroup, poSrcDS, |
1506 | 0 | mapSrcToDstDims, mapDstDimFullNames, psOptions)) |
1507 | 0 | { |
1508 | 0 | return false; |
1509 | 0 | } |
1510 | | |
1511 | | // If this array is the indexing variable of a dimension, link them |
1512 | | // together. |
1513 | 0 | auto srcArray = poSrcGroup->OpenMDArray(name); |
1514 | 0 | CPLAssert(srcArray); |
1515 | 0 | auto dstArray = poDstGroup->OpenMDArray(name); |
1516 | 0 | CPLAssert(dstArray); |
1517 | 0 | auto oIterDimName = |
1518 | 0 | mapSrcVariableNameToIndexedDimName.find(srcArray->GetName()); |
1519 | 0 | if (oIterDimName != mapSrcVariableNameToIndexedDimName.end()) |
1520 | 0 | { |
1521 | 0 | auto oCorrespondingDimIter = |
1522 | 0 | mapSrcToDstDims.find(oIterDimName->second); |
1523 | 0 | if (oCorrespondingDimIter != mapSrcToDstDims.end()) |
1524 | 0 | { |
1525 | 0 | CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler); |
1526 | 0 | oCorrespondingDimIter->second->SetIndexingVariable( |
1527 | 0 | std::move(dstArray)); |
1528 | 0 | } |
1529 | 0 | } |
1530 | 0 | } |
1531 | | |
1532 | 0 | if (bRecursive) |
1533 | 0 | { |
1534 | 0 | auto groupNames = poSrcGroup->GetGroupNames(); |
1535 | 0 | for (const auto &name : groupNames) |
1536 | 0 | { |
1537 | 0 | auto srcSubGroup = poSrcGroup->OpenGroup(name); |
1538 | 0 | if (!srcSubGroup) |
1539 | 0 | { |
1540 | 0 | return false; |
1541 | 0 | } |
1542 | 0 | auto dstSubGroup = poDstGroup->CreateVRTGroup(name); |
1543 | 0 | if (!dstSubGroup) |
1544 | 0 | { |
1545 | 0 | return false; |
1546 | 0 | } |
1547 | 0 | if (!CopyGroup(oDimRemapper, poDstRootGroup, dstSubGroup, |
1548 | 0 | poSrcRootGroup, srcSubGroup, poSrcDS, |
1549 | 0 | mapSrcToDstDims, mapDstDimFullNames, psOptions, |
1550 | 0 | true)) |
1551 | 0 | { |
1552 | 0 | return false; |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | } |
1556 | 0 | return true; |
1557 | 0 | } |
1558 | | |
1559 | | /************************************************************************/ |
1560 | | /* ParseGroupSpec() */ |
1561 | | /************************************************************************/ |
1562 | | |
1563 | | // foo |
1564 | | // name=foo,dstname=bar,recursive=no |
1565 | | static bool ParseGroupSpec(const std::string &groupSpec, std::string &srcName, |
1566 | | std::string &dstName, bool &bRecursive) |
1567 | 0 | { |
1568 | 0 | bRecursive = true; |
1569 | 0 | if (!STARTS_WITH(groupSpec.c_str(), "name=")) |
1570 | 0 | { |
1571 | 0 | srcName = groupSpec; |
1572 | 0 | return true; |
1573 | 0 | } |
1574 | | |
1575 | 0 | CPLStringList aosTokens(CSLTokenizeString2(groupSpec.c_str(), ",", 0)); |
1576 | 0 | for (int i = 0; i < aosTokens.size(); i++) |
1577 | 0 | { |
1578 | 0 | const std::string token(aosTokens[i]); |
1579 | 0 | if (STARTS_WITH(token.c_str(), "name=")) |
1580 | 0 | { |
1581 | 0 | srcName = token.substr(strlen("name=")); |
1582 | 0 | } |
1583 | 0 | else if (STARTS_WITH(token.c_str(), "dstname=")) |
1584 | 0 | { |
1585 | 0 | dstName = token.substr(strlen("dstname=")); |
1586 | 0 | } |
1587 | 0 | else if (token == "recursive=no") |
1588 | 0 | { |
1589 | 0 | bRecursive = false; |
1590 | 0 | } |
1591 | 0 | else |
1592 | 0 | { |
1593 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1594 | 0 | "Unexpected group specification part: %s", token.c_str()); |
1595 | 0 | return false; |
1596 | 0 | } |
1597 | 0 | } |
1598 | 0 | return true; |
1599 | 0 | } |
1600 | | |
1601 | | /************************************************************************/ |
1602 | | /* TranslateInternal() */ |
1603 | | /************************************************************************/ |
1604 | | |
1605 | | static bool TranslateInternal(std::shared_ptr<VRTGroup> &poDstRootGroup, |
1606 | | GDALDataset *poSrcDS, |
1607 | | const GDALMultiDimTranslateOptions *psOptions) |
1608 | 0 | { |
1609 | |
|
1610 | 0 | auto poSrcRootGroup = poSrcDS->GetRootGroup(); |
1611 | 0 | if (poSrcRootGroup) |
1612 | 0 | { |
1613 | 0 | if (psOptions->aosGroup.empty()) |
1614 | 0 | { |
1615 | 0 | auto attrs = poSrcRootGroup->GetAttributes(); |
1616 | 0 | for (const auto &attr : attrs) |
1617 | 0 | { |
1618 | 0 | if (attr->GetName() == "Conventions") |
1619 | 0 | continue; |
1620 | 0 | auto dstAttr = poDstRootGroup->CreateAttribute( |
1621 | 0 | attr->GetName(), attr->GetDimensionsSize(), |
1622 | 0 | attr->GetDataType()); |
1623 | 0 | if (dstAttr) |
1624 | 0 | { |
1625 | 0 | auto raw(attr->ReadAsRaw()); |
1626 | 0 | dstAttr->Write(raw.data(), raw.size()); |
1627 | 0 | } |
1628 | 0 | } |
1629 | 0 | } |
1630 | 0 | } |
1631 | |
|
1632 | 0 | DimensionRemapper oDimRemapper; |
1633 | 0 | std::map<std::string, std::shared_ptr<GDALDimension>> mapSrcToDstDims; |
1634 | 0 | std::map<std::string, std::shared_ptr<GDALDimension>> mapDstDimFullNames; |
1635 | 0 | if (!psOptions->aosGroup.empty()) |
1636 | 0 | { |
1637 | 0 | if (poSrcRootGroup == nullptr) |
1638 | 0 | { |
1639 | 0 | CPLError( |
1640 | 0 | CE_Failure, CPLE_AppDefined, |
1641 | 0 | "No multidimensional source dataset: -group cannot be used"); |
1642 | 0 | return false; |
1643 | 0 | } |
1644 | 0 | if (psOptions->aosGroup.size() == 1) |
1645 | 0 | { |
1646 | 0 | std::string srcName; |
1647 | 0 | std::string dstName; |
1648 | 0 | bool bRecursive; |
1649 | 0 | if (!ParseGroupSpec(psOptions->aosGroup[0], srcName, dstName, |
1650 | 0 | bRecursive)) |
1651 | 0 | return false; |
1652 | 0 | auto poSrcGroup = GetGroup(poSrcRootGroup, srcName); |
1653 | 0 | if (!poSrcGroup) |
1654 | 0 | return false; |
1655 | 0 | return CopyGroup(oDimRemapper, poDstRootGroup, poDstRootGroup, |
1656 | 0 | poSrcRootGroup, poSrcGroup, poSrcDS, |
1657 | 0 | mapSrcToDstDims, mapDstDimFullNames, psOptions, |
1658 | 0 | bRecursive); |
1659 | 0 | } |
1660 | 0 | else |
1661 | 0 | { |
1662 | 0 | for (const auto &osGroupSpec : psOptions->aosGroup) |
1663 | 0 | { |
1664 | 0 | std::string srcName; |
1665 | 0 | std::string dstName; |
1666 | 0 | bool bRecursive; |
1667 | 0 | if (!ParseGroupSpec(osGroupSpec, srcName, dstName, bRecursive)) |
1668 | 0 | return false; |
1669 | 0 | auto poSrcGroup = GetGroup(poSrcRootGroup, srcName); |
1670 | 0 | if (!poSrcGroup) |
1671 | 0 | return false; |
1672 | 0 | if (dstName.empty()) |
1673 | 0 | dstName = poSrcGroup->GetName(); |
1674 | 0 | auto dstSubGroup = poDstRootGroup->CreateVRTGroup(dstName); |
1675 | 0 | if (!dstSubGroup || |
1676 | 0 | !CopyGroup(oDimRemapper, poDstRootGroup, dstSubGroup, |
1677 | 0 | poSrcRootGroup, poSrcGroup, poSrcDS, |
1678 | 0 | mapSrcToDstDims, mapDstDimFullNames, psOptions, |
1679 | 0 | bRecursive)) |
1680 | 0 | { |
1681 | 0 | return false; |
1682 | 0 | } |
1683 | 0 | } |
1684 | 0 | } |
1685 | 0 | } |
1686 | 0 | else if (!psOptions->aosArraySpec.empty()) |
1687 | 0 | { |
1688 | 0 | for (const auto &arraySpec : psOptions->aosArraySpec) |
1689 | 0 | { |
1690 | 0 | if (!TranslateArray(oDimRemapper, nullptr, arraySpec, |
1691 | 0 | poSrcRootGroup, poSrcRootGroup, poDstRootGroup, |
1692 | 0 | poDstRootGroup, poSrcDS, mapSrcToDstDims, |
1693 | 0 | mapDstDimFullNames, psOptions)) |
1694 | 0 | { |
1695 | 0 | return false; |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | } |
1699 | 0 | else |
1700 | 0 | { |
1701 | 0 | if (poSrcRootGroup == nullptr) |
1702 | 0 | { |
1703 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1704 | 0 | "No multidimensional source dataset"); |
1705 | 0 | return false; |
1706 | 0 | } |
1707 | 0 | return CopyGroup(oDimRemapper, poDstRootGroup, poDstRootGroup, |
1708 | 0 | poSrcRootGroup, poSrcRootGroup, poSrcDS, |
1709 | 0 | mapSrcToDstDims, mapDstDimFullNames, psOptions, true); |
1710 | 0 | } |
1711 | | |
1712 | 0 | return true; |
1713 | 0 | } |
1714 | | |
1715 | | /************************************************************************/ |
1716 | | /* CopyToNonMultiDimensionalDriver() */ |
1717 | | /************************************************************************/ |
1718 | | |
1719 | | static GDALDatasetH |
1720 | | CopyToNonMultiDimensionalDriver(GDALDriver *poDriver, const char *pszDest, |
1721 | | const std::shared_ptr<GDALGroup> &poRG, |
1722 | | const GDALMultiDimTranslateOptions *psOptions) |
1723 | 0 | { |
1724 | 0 | std::shared_ptr<GDALMDArray> srcArray; |
1725 | 0 | if (psOptions && !psOptions->aosArraySpec.empty()) |
1726 | 0 | { |
1727 | 0 | if (psOptions->aosArraySpec.size() != 1) |
1728 | 0 | { |
1729 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1730 | 0 | "For output to a non-multidimensional driver, only " |
1731 | 0 | "one array should be specified"); |
1732 | 0 | return nullptr; |
1733 | 0 | } |
1734 | 0 | std::string srcArrayName; |
1735 | 0 | std::string dstArrayName; |
1736 | 0 | int band = -1; |
1737 | 0 | std::vector<int> anTransposedAxis; |
1738 | 0 | std::string viewExpr; |
1739 | 0 | GDALExtendedDataType outputType( |
1740 | 0 | GDALExtendedDataType::Create(GDT_Unknown)); |
1741 | 0 | bool bResampled = false; |
1742 | 0 | ParseArraySpec(psOptions->aosArraySpec[0], srcArrayName, dstArrayName, |
1743 | 0 | band, anTransposedAxis, viewExpr, outputType, |
1744 | 0 | bResampled); |
1745 | 0 | srcArray = poRG->OpenMDArray(dstArrayName); |
1746 | 0 | } |
1747 | 0 | else |
1748 | 0 | { |
1749 | 0 | auto srcArrayNames = poRG->GetMDArrayNames( |
1750 | 0 | psOptions ? psOptions->aosArrayOptions.List() : nullptr); |
1751 | 0 | for (const auto &srcArrayName : srcArrayNames) |
1752 | 0 | { |
1753 | 0 | auto tmpArray = poRG->OpenMDArray(srcArrayName); |
1754 | 0 | if (tmpArray) |
1755 | 0 | { |
1756 | 0 | const auto &dims(tmpArray->GetDimensions()); |
1757 | 0 | if (!(dims.size() == 1 && dims[0]->GetIndexingVariable() && |
1758 | 0 | dims[0]->GetIndexingVariable()->GetName() == |
1759 | 0 | srcArrayName)) |
1760 | 0 | { |
1761 | 0 | if (srcArray) |
1762 | 0 | { |
1763 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1764 | 0 | "Several arrays exist. Select one for " |
1765 | 0 | "output to non-multidimensional driver"); |
1766 | 0 | return nullptr; |
1767 | 0 | } |
1768 | 0 | srcArray = std::move(tmpArray); |
1769 | 0 | } |
1770 | 0 | } |
1771 | 0 | } |
1772 | 0 | } |
1773 | 0 | if (!srcArray) |
1774 | 0 | { |
1775 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find source array"); |
1776 | 0 | return nullptr; |
1777 | 0 | } |
1778 | 0 | size_t iXDim = static_cast<size_t>(-1); |
1779 | 0 | size_t iYDim = static_cast<size_t>(-1); |
1780 | 0 | const auto &dims(srcArray->GetDimensions()); |
1781 | 0 | for (size_t i = 0; i < dims.size(); ++i) |
1782 | 0 | { |
1783 | 0 | if (dims[i]->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X) |
1784 | 0 | { |
1785 | 0 | iXDim = i; |
1786 | 0 | } |
1787 | 0 | else if (dims[i]->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y) |
1788 | 0 | { |
1789 | 0 | iYDim = i; |
1790 | 0 | } |
1791 | 0 | } |
1792 | 0 | if (dims.size() == 1) |
1793 | 0 | { |
1794 | 0 | iXDim = 0; |
1795 | 0 | } |
1796 | 0 | else if (dims.size() >= 2 && (iXDim == static_cast<size_t>(-1) || |
1797 | 0 | iYDim == static_cast<size_t>(-1))) |
1798 | 0 | { |
1799 | 0 | iXDim = dims.size() - 1; |
1800 | 0 | iYDim = dims.size() - 2; |
1801 | 0 | } |
1802 | 0 | std::unique_ptr<GDALDataset> poTmpSrcDS( |
1803 | 0 | srcArray->AsClassicDataset(iXDim, iYDim)); |
1804 | 0 | if (!poTmpSrcDS) |
1805 | 0 | return nullptr; |
1806 | 0 | return GDALDataset::ToHandle(poDriver->CreateCopy( |
1807 | 0 | pszDest, poTmpSrcDS.get(), false, |
1808 | 0 | psOptions ? const_cast<char **>(psOptions->aosCreateOptions.List()) |
1809 | 0 | : nullptr, |
1810 | 0 | psOptions ? psOptions->pfnProgress : nullptr, |
1811 | 0 | psOptions ? psOptions->pProgressData : nullptr)); |
1812 | 0 | } |
1813 | | |
1814 | | /************************************************************************/ |
1815 | | /* GDALMultiDimTranslate() */ |
1816 | | /************************************************************************/ |
1817 | | |
1818 | | /* clang-format off */ |
1819 | | /** |
1820 | | * Converts raster data between different formats. |
1821 | | * |
1822 | | * This is the equivalent of the |
1823 | | * <a href="/programs/gdalmdimtranslate.html">gdalmdimtranslate</a> utility. |
1824 | | * |
1825 | | * GDALMultiDimTranslateOptions* must be allocated and freed with |
1826 | | * GDALMultiDimTranslateOptionsNew() and GDALMultiDimTranslateOptionsFree() |
1827 | | * respectively. pszDest and hDstDS cannot be used at the same time. |
1828 | | * |
1829 | | * @param pszDest the destination dataset path or NULL. |
1830 | | * @param hDstDS the destination dataset or NULL. |
1831 | | * @param nSrcCount the number of input datasets. |
1832 | | * @param pahSrcDS the list of input datasets. |
1833 | | * @param psOptions the options struct returned by |
1834 | | * GDALMultiDimTranslateOptionsNew() or NULL. |
1835 | | * @param pbUsageError pointer to a integer output variable to store if any |
1836 | | * usage error has occurred or NULL. |
1837 | | * @return the output dataset (new dataset that must be closed using |
1838 | | * GDALClose(), or hDstDS is not NULL) or NULL in case of error. |
1839 | | * |
1840 | | * @since GDAL 3.1 |
1841 | | */ |
1842 | | /* clang-format on */ |
1843 | | |
1844 | | GDALDatasetH |
1845 | | GDALMultiDimTranslate(const char *pszDest, GDALDatasetH hDstDS, int nSrcCount, |
1846 | | GDALDatasetH *pahSrcDS, |
1847 | | const GDALMultiDimTranslateOptions *psOptions, |
1848 | | int *pbUsageError) |
1849 | 0 | { |
1850 | 0 | if (pbUsageError) |
1851 | 0 | *pbUsageError = false; |
1852 | 0 | if (nSrcCount != 1 || pahSrcDS[0] == nullptr) |
1853 | 0 | { |
1854 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1855 | 0 | "Only one source dataset is supported"); |
1856 | 0 | if (pbUsageError) |
1857 | 0 | *pbUsageError = true; |
1858 | 0 | return nullptr; |
1859 | 0 | } |
1860 | | |
1861 | 0 | if (hDstDS) |
1862 | 0 | { |
1863 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1864 | 0 | "Update of existing file not supported yet"); |
1865 | 0 | GDALClose(hDstDS); |
1866 | 0 | return nullptr; |
1867 | 0 | } |
1868 | | |
1869 | 0 | CPLString osFormat(psOptions ? psOptions->osFormat : ""); |
1870 | 0 | if (pszDest == nullptr /* && hDstDS == nullptr */) |
1871 | 0 | { |
1872 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1873 | 0 | "Both pszDest and hDstDS are NULL."); |
1874 | 0 | if (pbUsageError) |
1875 | 0 | *pbUsageError = true; |
1876 | 0 | return nullptr; |
1877 | 0 | } |
1878 | | |
1879 | 0 | GDALDriver *poDriver = nullptr; |
1880 | |
|
1881 | | #ifdef this_is_dead_code_for_now |
1882 | | const bool bCloseOutDSOnError = hDstDS == nullptr; |
1883 | | if (pszDest == nullptr) |
1884 | | pszDest = GDALGetDescription(hDstDS); |
1885 | | #endif |
1886 | |
|
1887 | 0 | if (psOptions && psOptions->bOverwrite && !EQUAL(pszDest, "")) |
1888 | 0 | { |
1889 | 0 | VSIRmdirRecursive(pszDest); |
1890 | 0 | } |
1891 | 0 | else if (psOptions && psOptions->bNoOverwrite && !EQUAL(pszDest, "")) |
1892 | 0 | { |
1893 | 0 | VSIStatBufL sStat; |
1894 | 0 | if (VSIStatL(pszDest, &sStat) == 0) |
1895 | 0 | { |
1896 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1897 | 0 | "File '%s' already exists. Specify the --overwrite " |
1898 | 0 | "option to overwrite it.", |
1899 | 0 | pszDest); |
1900 | 0 | return nullptr; |
1901 | 0 | } |
1902 | 0 | else if (std::unique_ptr<GDALDataset>(GDALDataset::Open(pszDest))) |
1903 | 0 | { |
1904 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1905 | 0 | "Dataset '%s' already exists. Specify the --overwrite " |
1906 | 0 | "option to overwrite it.", |
1907 | 0 | pszDest); |
1908 | 0 | return nullptr; |
1909 | 0 | } |
1910 | 0 | } |
1911 | | |
1912 | | #ifdef this_is_dead_code_for_now |
1913 | | if (hDstDS == nullptr) |
1914 | | #endif |
1915 | 0 | { |
1916 | 0 | if (osFormat.empty()) |
1917 | 0 | { |
1918 | 0 | if (EQUAL(CPLGetExtensionSafe(pszDest).c_str(), "nc")) |
1919 | 0 | osFormat = "netCDF"; |
1920 | 0 | else |
1921 | 0 | osFormat = GetOutputDriverForRaster(pszDest); |
1922 | 0 | if (osFormat.empty()) |
1923 | 0 | { |
1924 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1925 | 0 | "Cannot determine output driver for dataset name '%s'", |
1926 | 0 | pszDest); |
1927 | 0 | return nullptr; |
1928 | 0 | } |
1929 | 0 | } |
1930 | 0 | poDriver = GDALDriver::FromHandle(GDALGetDriverByName(osFormat)); |
1931 | 0 | CSLConstList papszDriverMD = |
1932 | 0 | poDriver ? poDriver->GetMetadata() : nullptr; |
1933 | 0 | if (poDriver == nullptr || |
1934 | 0 | (!CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_RASTER, |
1935 | 0 | "FALSE")) && |
1936 | 0 | !CPLTestBool(CSLFetchNameValueDef( |
1937 | 0 | papszDriverMD, GDAL_DCAP_MULTIDIM_RASTER, "FALSE"))) || |
1938 | 0 | (!CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, |
1939 | 0 | "FALSE")) && |
1940 | 0 | !CPLTestBool(CSLFetchNameValueDef( |
1941 | 0 | papszDriverMD, GDAL_DCAP_CREATECOPY, "FALSE")) && |
1942 | 0 | !CPLTestBool(CSLFetchNameValueDef( |
1943 | 0 | papszDriverMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "FALSE")) && |
1944 | 0 | !CPLTestBool(CSLFetchNameValueDef( |
1945 | 0 | papszDriverMD, GDAL_DCAP_CREATECOPY_MULTIDIMENSIONAL, |
1946 | 0 | "FALSE")))) |
1947 | 0 | { |
1948 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1949 | 0 | "Output driver `%s' not recognised or does not support " |
1950 | 0 | "output file creation.", |
1951 | 0 | osFormat.c_str()); |
1952 | 0 | return nullptr; |
1953 | 0 | } |
1954 | 0 | } |
1955 | | |
1956 | 0 | GDALDataset *poSrcDS = GDALDataset::FromHandle(pahSrcDS[0]); |
1957 | |
|
1958 | 0 | std::unique_ptr<GDALDataset> poTmpDS; |
1959 | 0 | GDALDataset *poTmpSrcDS = poSrcDS; |
1960 | 0 | if (psOptions && |
1961 | 0 | (!psOptions->aosArraySpec.empty() || !psOptions->aosGroup.empty() || |
1962 | 0 | !psOptions->aosSubset.empty() || !psOptions->aosScaleFactor.empty() || |
1963 | 0 | !psOptions->aosArrayOptions.empty())) |
1964 | 0 | { |
1965 | 0 | auto poVRTDS = |
1966 | 0 | VRTDataset::CreateVRTMultiDimensional("", nullptr, nullptr); |
1967 | 0 | CPLAssert(poVRTDS); |
1968 | | |
1969 | 0 | auto poDstRootGroup = poVRTDS->GetRootVRTGroup(); |
1970 | 0 | CPLAssert(poDstRootGroup); |
1971 | | |
1972 | 0 | if (!TranslateInternal(poDstRootGroup, poSrcDS, psOptions)) |
1973 | 0 | { |
1974 | | #ifdef this_is_dead_code_for_now |
1975 | | if (bCloseOutDSOnError) |
1976 | | #endif |
1977 | 0 | { |
1978 | 0 | GDALClose(hDstDS); |
1979 | 0 | hDstDS = nullptr; |
1980 | 0 | } |
1981 | 0 | return nullptr; |
1982 | 0 | } |
1983 | | |
1984 | 0 | poTmpDS = std::move(poVRTDS); |
1985 | 0 | poTmpSrcDS = poTmpDS.get(); |
1986 | 0 | } |
1987 | | |
1988 | 0 | auto poRG(poTmpSrcDS->GetRootGroup()); |
1989 | 0 | if (poRG && |
1990 | 0 | poDriver->GetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL) == |
1991 | 0 | nullptr && |
1992 | 0 | poDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY_MULTIDIMENSIONAL) == |
1993 | 0 | nullptr) |
1994 | 0 | { |
1995 | | #ifdef this_is_dead_code_for_now |
1996 | | if (hDstDS) |
1997 | | { |
1998 | | CPLError(CE_Failure, CPLE_NotSupported, |
1999 | | "Appending to non-multidimensional driver not supported."); |
2000 | | GDALClose(hDstDS); |
2001 | | hDstDS = nullptr; |
2002 | | return nullptr; |
2003 | | } |
2004 | | #endif |
2005 | 0 | hDstDS = |
2006 | 0 | CopyToNonMultiDimensionalDriver(poDriver, pszDest, poRG, psOptions); |
2007 | 0 | } |
2008 | 0 | else |
2009 | 0 | { |
2010 | 0 | hDstDS = GDALDataset::ToHandle(poDriver->CreateCopy( |
2011 | 0 | pszDest, poTmpSrcDS, false, |
2012 | 0 | psOptions ? const_cast<char **>(psOptions->aosCreateOptions.List()) |
2013 | 0 | : nullptr, |
2014 | 0 | psOptions ? psOptions->pfnProgress : nullptr, |
2015 | 0 | psOptions ? psOptions->pProgressData : nullptr)); |
2016 | 0 | } |
2017 | |
|
2018 | 0 | return hDstDS; |
2019 | 0 | } |
2020 | | |
2021 | | /************************************************************************/ |
2022 | | /* GDALMultiDimTranslateOptionsNew() */ |
2023 | | /************************************************************************/ |
2024 | | |
2025 | | /** |
2026 | | * Allocates a GDALMultiDimTranslateOptions struct. |
2027 | | * |
2028 | | * @param papszArgv NULL terminated list of options (potentially including |
2029 | | * filename and open options too), or NULL. The accepted options are the ones of |
2030 | | * the <a href="/programs/gdalmdimtranslate.html">gdalmdimtranslate</a> utility. |
2031 | | * @param psOptionsForBinary should be nullptr, unless called from |
2032 | | * gdalmdimtranslate_bin.cpp |
2033 | | * @return pointer to the allocated GDALMultiDimTranslateOptions struct. Must be |
2034 | | * freed with GDALMultiDimTranslateOptionsFree(). |
2035 | | * |
2036 | | * @since GDAL 3.1 |
2037 | | */ |
2038 | | |
2039 | | GDALMultiDimTranslateOptions *GDALMultiDimTranslateOptionsNew( |
2040 | | char **papszArgv, GDALMultiDimTranslateOptionsForBinary *psOptionsForBinary) |
2041 | 0 | { |
2042 | |
|
2043 | 0 | auto psOptions = std::make_unique<GDALMultiDimTranslateOptions>(); |
2044 | | |
2045 | | /* -------------------------------------------------------------------- */ |
2046 | | /* Parse arguments. */ |
2047 | | /* -------------------------------------------------------------------- */ |
2048 | 0 | try |
2049 | 0 | { |
2050 | 0 | auto argParser = GDALMultiDimTranslateAppOptionsGetParser( |
2051 | 0 | psOptions.get(), psOptionsForBinary); |
2052 | |
|
2053 | 0 | argParser->parse_args_without_binary_name(papszArgv); |
2054 | | |
2055 | | // Check for invalid options: |
2056 | | // -scaleaxes is not compatible with -array = "view" |
2057 | | // -subset is not compatible with -array = "view" |
2058 | 0 | if (std::find(psOptions->aosArraySpec.cbegin(), |
2059 | 0 | psOptions->aosArraySpec.cend(), |
2060 | 0 | "view") != psOptions->aosArraySpec.cend()) |
2061 | 0 | { |
2062 | 0 | if (!psOptions->aosScaleFactor.empty()) |
2063 | 0 | { |
2064 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2065 | 0 | "The -scaleaxes option is not compatible with the " |
2066 | 0 | "-array \"view\" option."); |
2067 | 0 | return nullptr; |
2068 | 0 | } |
2069 | | |
2070 | 0 | if (!psOptions->aosSubset.empty()) |
2071 | 0 | { |
2072 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2073 | 0 | "The -subset option is not compatible with the -array " |
2074 | 0 | "\"view\" option."); |
2075 | 0 | return nullptr; |
2076 | 0 | } |
2077 | 0 | } |
2078 | 0 | } |
2079 | 0 | catch (const std::exception &error) |
2080 | 0 | { |
2081 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what()); |
2082 | 0 | return nullptr; |
2083 | 0 | } |
2084 | | |
2085 | 0 | if (psOptionsForBinary) |
2086 | 0 | { |
2087 | | // Note: bUpdate is apparently never changed by the command line options |
2088 | 0 | psOptionsForBinary->bUpdate = psOptions->bUpdate; |
2089 | 0 | if (!psOptions->osFormat.empty()) |
2090 | 0 | psOptionsForBinary->osFormat = psOptions->osFormat; |
2091 | 0 | } |
2092 | |
|
2093 | 0 | return psOptions.release(); |
2094 | 0 | } |
2095 | | |
2096 | | /************************************************************************/ |
2097 | | /* GDALMultiDimTranslateOptionsFree() */ |
2098 | | /************************************************************************/ |
2099 | | |
2100 | | /** |
2101 | | * Frees the GDALMultiDimTranslateOptions struct. |
2102 | | * |
2103 | | * @param psOptions the options struct for GDALMultiDimTranslate(). |
2104 | | * |
2105 | | * @since GDAL 3.1 |
2106 | | */ |
2107 | | |
2108 | | void GDALMultiDimTranslateOptionsFree(GDALMultiDimTranslateOptions *psOptions) |
2109 | 0 | { |
2110 | 0 | delete psOptions; |
2111 | 0 | } |
2112 | | |
2113 | | /************************************************************************/ |
2114 | | /* GDALMultiDimTranslateOptionsSetProgress() */ |
2115 | | /************************************************************************/ |
2116 | | |
2117 | | /** |
2118 | | * Set a progress function. |
2119 | | * |
2120 | | * @param psOptions the options struct for GDALMultiDimTranslate(). |
2121 | | * @param pfnProgress the progress callback. |
2122 | | * @param pProgressData the user data for the progress callback. |
2123 | | * |
2124 | | * @since GDAL 3.1 |
2125 | | */ |
2126 | | |
2127 | | void GDALMultiDimTranslateOptionsSetProgress( |
2128 | | GDALMultiDimTranslateOptions *psOptions, GDALProgressFunc pfnProgress, |
2129 | | void *pProgressData) |
2130 | 0 | { |
2131 | 0 | psOptions->pfnProgress = pfnProgress; |
2132 | 0 | psOptions->pProgressData = pProgressData; |
2133 | 0 | } |