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