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