Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim_group.cpp
4 : * Project: GDAL Core
5 : * Purpose: Implementation of GDALGroup class
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_float.h"
15 : #include "gdal_multidim.h"
16 :
17 : #include <list>
18 : #include <map>
19 : #include <queue>
20 : #include <set>
21 :
22 : /************************************************************************/
23 : /* GDALGroup() */
24 : /************************************************************************/
25 :
26 : //! @cond Doxygen_Suppress
27 14015 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
28 14015 : const std::string &osContext)
29 14015 : : m_osName(osParentName.empty() ? "/" : osName),
30 : m_osFullName(
31 28030 : !osParentName.empty()
32 21057 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
33 : : "/"),
34 35072 : m_osContext(osContext)
35 : {
36 14015 : }
37 :
38 : //! @endcond
39 :
40 : /************************************************************************/
41 : /* ~GDALGroup() */
42 : /************************************************************************/
43 :
44 : GDALGroup::~GDALGroup() = default;
45 :
46 : /************************************************************************/
47 : /* GetMDArrayNames() */
48 : /************************************************************************/
49 :
50 : /** Return the list of multidimensional array names contained in this group.
51 : *
52 : * @note Driver implementation: optionally implemented. If implemented,
53 : * OpenMDArray() should also be implemented.
54 : *
55 : * Drivers known to implement it: MEM, netCDF.
56 : *
57 : * This is the same as the C function GDALGroupGetMDArrayNames().
58 : *
59 : * @param papszOptions Driver specific options determining how arrays
60 : * should be retrieved. Pass nullptr for default behavior.
61 : *
62 : * @return the array names.
63 : */
64 : std::vector<std::string>
65 10 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
66 : {
67 10 : return {};
68 : }
69 :
70 : /************************************************************************/
71 : /* GetMDArrayFullNamesRecursive() */
72 : /************************************************************************/
73 :
74 : /** Return the list of multidimensional array full names contained in this
75 : * group and its subgroups.
76 : *
77 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
78 : *
79 : * @param papszGroupDiscoverOptions Driver specific options determining how groups
80 : * should be retrieved. Pass nullptr for default behavior.
81 : * @param papszArrayDiscoverOptions Driver specific options determining how arrays
82 : * should be retrieved. Pass nullptr for default behavior.
83 : * @param papszGroupOpenOptions Driver specific options determining how a group should
84 : * be opened. Pass nullptr for default behavior.
85 : *
86 : * @return the array full names.
87 : *
88 : * @since 3.11
89 : */
90 31 : std::vector<std::string> GDALGroup::GetMDArrayFullNamesRecursive(
91 : CSLConstList papszGroupDiscoverOptions,
92 : CSLConstList papszArrayDiscoverOptions,
93 : CSLConstList papszGroupOpenOptions) const
94 : {
95 31 : std::vector<std::string> ret;
96 62 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
97 31 : stackGroups.push_back(nullptr); // nullptr means this
98 65 : while (!stackGroups.empty())
99 : {
100 68 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
101 34 : stackGroups.erase(stackGroups.begin());
102 34 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
103 85 : for (const std::string &arrayName :
104 204 : poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
105 : {
106 170 : std::string osFullName = poCurGroup->GetFullName();
107 85 : if (!osFullName.empty() && osFullName.back() != '/')
108 3 : osFullName += '/';
109 85 : osFullName += arrayName;
110 85 : ret.push_back(std::move(osFullName));
111 : }
112 34 : auto insertionPoint = stackGroups.begin();
113 3 : for (const auto &osSubGroup :
114 40 : poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
115 : {
116 : auto poSubGroup =
117 6 : poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
118 3 : if (poSubGroup)
119 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
120 : }
121 : }
122 :
123 62 : return ret;
124 : }
125 :
126 : /************************************************************************/
127 : /* OpenMDArray() */
128 : /************************************************************************/
129 :
130 : /** Open and return a multidimensional array.
131 : *
132 : * @note Driver implementation: optionally implemented. If implemented,
133 : * GetMDArrayNames() should also be implemented.
134 : *
135 : * Drivers known to implement it: MEM, netCDF.
136 : *
137 : * This is the same as the C function GDALGroupOpenMDArray().
138 : *
139 : * @param osName Array name.
140 : * @param papszOptions Driver specific options determining how the array should
141 : * be opened. Pass nullptr for default behavior.
142 : *
143 : * @return the array, or nullptr.
144 : */
145 : std::shared_ptr<GDALMDArray>
146 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
147 : CPL_UNUSED CSLConstList papszOptions) const
148 : {
149 0 : return nullptr;
150 : }
151 :
152 : /************************************************************************/
153 : /* GetMDArrays() */
154 : /************************************************************************/
155 :
156 : /** Open and return all multidimensional arrays of this group.
157 : *
158 : * @param papszDiscoverOptions Driver specific options determining how arrays
159 : * should be retrieved. Pass nullptr for default behavior.
160 : * @param papszOpenOptions Driver specific options determining how an array should
161 : * be opened. Pass nullptr for default behavior.
162 : *
163 : * @return the arrays
164 : * @since GDAL 3.14
165 : */
166 : std::vector<std::shared_ptr<GDALMDArray>>
167 0 : GDALGroup::GetMDArrays(CSLConstList papszDiscoverOptions,
168 : CSLConstList papszOpenOptions) const
169 : {
170 0 : std::vector<std::shared_ptr<GDALMDArray>> ret;
171 0 : for (const auto &osName : GetMDArrayNames(papszDiscoverOptions))
172 : {
173 0 : if (auto poMDArray = OpenMDArray(osName, papszOpenOptions))
174 0 : ret.push_back(std::move(poMDArray));
175 : }
176 0 : return ret;
177 : }
178 :
179 : /************************************************************************/
180 : /* GetMDArraysRecursive() */
181 : /************************************************************************/
182 :
183 : /** Open and return all multidimensional arrays of this group and its subgroups.
184 : *
185 : * @param papszGroupDiscoverOptions Driver specific options determining how groups
186 : * should be retrieved. Pass nullptr for default behavior.
187 : * @param papszArrayDiscoverOptions Driver specific options determining how arrays
188 : * should be retrieved. Pass nullptr for default behavior.
189 : * @param papszGroupOpenOptions Driver specific options determining how a group should
190 : * be opened. Pass nullptr for default behavior.
191 : * @param papszArrayOpenOptions Driver specific options determining how an array should
192 : * be opened. Pass nullptr for default behavior.
193 : *
194 : * @return the arrays
195 : * @since GDAL 3.14
196 : */
197 : std::vector<std::shared_ptr<GDALMDArray>>
198 6 : GDALGroup::GetMDArraysRecursive(CSLConstList papszGroupDiscoverOptions,
199 : CSLConstList papszArrayDiscoverOptions,
200 : CSLConstList papszGroupOpenOptions,
201 : CSLConstList papszArrayOpenOptions) const
202 : {
203 6 : std::vector<std::shared_ptr<GDALMDArray>> ret;
204 12 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
205 6 : stackGroups.push_back(nullptr); // nullptr means this
206 12 : while (!stackGroups.empty())
207 : {
208 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
209 6 : stackGroups.erase(stackGroups.begin());
210 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
211 19 : for (const std::string &arrayName :
212 44 : poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
213 : {
214 : auto poArray =
215 38 : poCurGroup->OpenMDArray(arrayName, papszArrayOpenOptions);
216 19 : if (poArray)
217 19 : ret.push_back(std::move(poArray));
218 : }
219 6 : auto insertionPoint = stackGroups.begin();
220 0 : for (const auto &osSubGroup :
221 6 : poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
222 : {
223 : auto poSubGroup =
224 0 : poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
225 0 : if (poSubGroup)
226 0 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
227 : }
228 : }
229 :
230 12 : return ret;
231 : }
232 :
233 : /************************************************************************/
234 : /* GetGroupNames() */
235 : /************************************************************************/
236 :
237 : /** Return the list of sub-groups contained in this group.
238 : *
239 : * @note Driver implementation: optionally implemented. If implemented,
240 : * OpenGroup() should also be implemented.
241 : *
242 : * Drivers known to implement it: MEM, netCDF.
243 : *
244 : * This is the same as the C function GDALGroupGetGroupNames().
245 : *
246 : * @param papszOptions Driver specific options determining how groups
247 : * should be retrieved. Pass nullptr for default behavior.
248 : *
249 : * @return the group names.
250 : */
251 : std::vector<std::string>
252 14 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
253 : {
254 14 : return {};
255 : }
256 :
257 : /************************************************************************/
258 : /* OpenGroup() */
259 : /************************************************************************/
260 :
261 : /** Open and return a sub-group.
262 : *
263 : * @note Driver implementation: optionally implemented. If implemented,
264 : * GetGroupNames() should also be implemented.
265 : *
266 : * Drivers known to implement it: MEM, netCDF.
267 : *
268 : * This is the same as the C function GDALGroupOpenGroup().
269 : *
270 : * @param osName Sub-group name.
271 : * @param papszOptions Driver specific options determining how the sub-group
272 : * should be opened. Pass nullptr for default behavior.
273 : *
274 : * @return the group, or nullptr.
275 : */
276 : std::shared_ptr<GDALGroup>
277 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
278 : CPL_UNUSED CSLConstList papszOptions) const
279 : {
280 4 : return nullptr;
281 : }
282 :
283 : /************************************************************************/
284 : /* GetGroups() */
285 : /************************************************************************/
286 :
287 : /** Open and return all subgroups of this group.
288 : *
289 : * @param papszDiscoverOptions Driver specific options determining how subgroups
290 : * should be retrieved. Pass nullptr for default behavior.
291 : * @param papszOpenOptions Driver specific options determining how a subgroup should
292 : * be opened. Pass nullptr for default behavior.
293 : *
294 : * @return the subgroups
295 : * @since GDAL 3.14
296 : */
297 : std::vector<std::shared_ptr<GDALGroup>>
298 0 : GDALGroup::GetGroups(CSLConstList papszDiscoverOptions,
299 : CSLConstList papszOpenOptions) const
300 : {
301 0 : std::vector<std::shared_ptr<GDALGroup>> ret;
302 0 : for (const auto &osName : GetGroupNames(papszDiscoverOptions))
303 : {
304 0 : if (auto poSubGroup = OpenGroup(osName, papszOpenOptions))
305 0 : ret.push_back(std::move(poSubGroup));
306 : }
307 0 : return ret;
308 : }
309 :
310 : /************************************************************************/
311 : /* GetMDArrayFullNamesRecursive() */
312 : /************************************************************************/
313 :
314 : /** Open and return all subgroups of this group, in a recursive way.
315 : *
316 : * @param papszDiscoverOptions Driver specific options determining how subgroups
317 : * should be retrieved. Pass nullptr for default behavior.
318 : * @param papszOpenOptions Driver specific options determining how a subgroup should
319 : * be opened. Pass nullptr for default behavior.
320 : *
321 : * @return the subgroups
322 : * @since GDAL 3.14
323 : */
324 : std::vector<std::shared_ptr<GDALGroup>>
325 4 : GDALGroup::GetGroupsRecursive(CSLConstList papszDiscoverOptions,
326 : CSLConstList papszOpenOptions) const
327 : {
328 4 : std::vector<std::shared_ptr<GDALGroup>> ret;
329 8 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
330 4 : stackGroups.push_back(nullptr); // nullptr means this
331 8 : while (!stackGroups.empty())
332 : {
333 8 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
334 4 : stackGroups.erase(stackGroups.begin());
335 4 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
336 4 : auto insertionPoint = stackGroups.begin();
337 0 : for (const auto &osSubGroup :
338 4 : poCurGroup->GetGroupNames(papszDiscoverOptions))
339 : {
340 : auto poSubGroup =
341 0 : poCurGroup->OpenGroup(osSubGroup, papszOpenOptions);
342 0 : if (poSubGroup)
343 : {
344 0 : ret.push_back(poSubGroup);
345 0 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
346 : }
347 : }
348 : }
349 :
350 8 : return ret;
351 : }
352 :
353 : /************************************************************************/
354 : /* GetVectorLayerNames() */
355 : /************************************************************************/
356 :
357 : /** Return the list of layer names contained in this group.
358 : *
359 : * @note Driver implementation: optionally implemented. If implemented,
360 : * OpenVectorLayer() should also be implemented.
361 : *
362 : * Drivers known to implement it: OpenFileGDB, FileGDB
363 : *
364 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
365 : * GDALDataset::GetLayer() should then be used.
366 : *
367 : * This is the same as the C function GDALGroupGetVectorLayerNames().
368 : *
369 : * @param papszOptions Driver specific options determining how layers
370 : * should be retrieved. Pass nullptr for default behavior.
371 : *
372 : * @return the vector layer names.
373 : * @since GDAL 3.4
374 : */
375 : std::vector<std::string>
376 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
377 : {
378 1 : return {};
379 : }
380 :
381 : /************************************************************************/
382 : /* OpenVectorLayer() */
383 : /************************************************************************/
384 :
385 : /** Open and return a vector layer.
386 : *
387 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
388 : * lifetime of the returned OGRLayer* is linked to the one of the owner
389 : * dataset (contrary to the general design of this class where objects can be
390 : * used independently of the object that returned them)
391 : *
392 : * @note Driver implementation: optionally implemented. If implemented,
393 : * GetVectorLayerNames() should also be implemented.
394 : *
395 : * Drivers known to implement it: MEM, netCDF.
396 : *
397 : * This is the same as the C function GDALGroupOpenVectorLayer().
398 : *
399 : * @param osName Vector layer name.
400 : * @param papszOptions Driver specific options determining how the layer should
401 : * be opened. Pass nullptr for default behavior.
402 : *
403 : * @return the group, or nullptr.
404 : */
405 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
406 : CPL_UNUSED CSLConstList papszOptions) const
407 : {
408 2 : return nullptr;
409 : }
410 :
411 : /************************************************************************/
412 : /* GetDimensions() */
413 : /************************************************************************/
414 :
415 : /** Return the list of dimensions contained in this group and used by its
416 : * arrays.
417 : *
418 : * This is for dimensions that can potentially be used by several arrays.
419 : * Not all drivers might implement this. To retrieve the dimensions used by
420 : * a specific array, use GDALMDArray::GetDimensions().
421 : *
422 : * Drivers known to implement it: MEM, netCDF
423 : *
424 : * This is the same as the C function GDALGroupGetDimensions().
425 : *
426 : * @param papszOptions Driver specific options determining how dimensions
427 : * should be retrieved. Pass nullptr for default behavior.
428 : *
429 : * @return the dimensions.
430 : */
431 : std::vector<std::shared_ptr<GDALDimension>>
432 26 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
433 : {
434 26 : return {};
435 : }
436 :
437 : /************************************************************************/
438 : /* GetDimensionsRecursive() */
439 : /************************************************************************/
440 :
441 : /** Return all dimensions arrays of this group, its arrays, its subgroups,
442 : * in a recursive way
443 : *
444 : * @param papszDimensionDiscoverOptions Driver specific options determining how dimensions
445 : * should be retrieved. Pass nullptr for default behavior.
446 : * @param papszGroupDiscoverOptions Driver specific options determining how groups
447 : * should be retrieved. Pass nullptr for default behavior.
448 : * @param papszArrayDiscoverOptions Driver specific options determining how arrays
449 : * should be retrieved. Pass nullptr for default behavior.
450 : * @param papszGroupOpenOptions Driver specific options determining how a group should
451 : * be opened. Pass nullptr for default behavior.
452 : * @param papszArrayOpenOptions Driver specific options determining how an array should
453 : * be opened. Pass nullptr for default behavior.
454 : *
455 : * @return the dimensions
456 : * @since GDAL 3.14
457 : */
458 : std::vector<std::shared_ptr<GDALDimension>>
459 4 : GDALGroup::GetDimensionsRecursive(CSLConstList papszDimensionDiscoverOptions,
460 : CSLConstList papszGroupDiscoverOptions,
461 : CSLConstList papszArrayDiscoverOptions,
462 : CSLConstList papszGroupOpenOptions,
463 : CSLConstList papszArrayOpenOptions) const
464 : {
465 8 : std::set<std::string> setDimensionNames;
466 4 : std::vector<std::shared_ptr<GDALDimension>> ret;
467 :
468 : const auto AddDimensions =
469 15 : [&setDimensionNames,
470 30 : &ret](const std::vector<std::shared_ptr<GDALDimension>> &apoDims)
471 : {
472 37 : for (const auto &poDim : apoDims)
473 : {
474 22 : const std::string &osName = poDim->GetFullName();
475 22 : if (osName.empty() || osName.front() != '/')
476 0 : ret.push_back(poDim);
477 22 : else if (setDimensionNames.insert(osName).second)
478 8 : ret.push_back(poDim);
479 : }
480 15 : };
481 :
482 8 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
483 4 : stackGroups.push_back(nullptr); // nullptr means this
484 8 : while (!stackGroups.empty())
485 : {
486 8 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
487 4 : stackGroups.erase(stackGroups.begin());
488 4 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
489 :
490 4 : AddDimensions(poCurGroup->GetDimensions(papszDimensionDiscoverOptions));
491 :
492 11 : for (const std::string &arrayName :
493 26 : poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
494 : {
495 22 : if (auto poMDArray = OpenMDArray(arrayName, papszArrayOpenOptions))
496 : {
497 11 : AddDimensions(poMDArray->GetDimensions());
498 : }
499 : }
500 4 : auto insertionPoint = stackGroups.begin();
501 0 : for (const auto &osSubGroup :
502 4 : poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
503 : {
504 : auto poSubGroup =
505 0 : poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
506 0 : if (poSubGroup)
507 0 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
508 : }
509 : }
510 8 : return ret;
511 : }
512 :
513 : /************************************************************************/
514 : /* GetStructuralInfo() */
515 : /************************************************************************/
516 :
517 : /** Return structural information on the group.
518 : *
519 : * This may be the compression, etc..
520 : *
521 : * The return value should not be freed and is valid until GDALGroup is
522 : * released or this function called again.
523 : *
524 : * This is the same as the C function GDALGroupGetStructuralInfo().
525 : */
526 76 : CSLConstList GDALGroup::GetStructuralInfo() const
527 : {
528 76 : return nullptr;
529 : }
530 :
531 : /************************************************************************/
532 : /* CreateGroup() */
533 : /************************************************************************/
534 :
535 : /** Create a sub-group within a group.
536 : *
537 : * Optionally implemented by drivers.
538 : *
539 : * Drivers known to implement it: MEM, netCDF
540 : *
541 : * This is the same as the C function GDALGroupCreateGroup().
542 : *
543 : * @param osName Sub-group name.
544 : * @param papszOptions Driver specific options determining how the sub-group
545 : * should be created.
546 : *
547 : * @return the new sub-group, or nullptr in case of error.
548 : */
549 : std::shared_ptr<GDALGroup>
550 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
551 : CPL_UNUSED CSLConstList papszOptions)
552 : {
553 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
554 0 : return nullptr;
555 : }
556 :
557 : /************************************************************************/
558 : /* DeleteGroup() */
559 : /************************************************************************/
560 :
561 : /** Delete a sub-group from a group.
562 : *
563 : * Optionally implemented.
564 : *
565 : * After this call, if a previously obtained instance of the deleted object
566 : * is still alive, no method other than for freeing it should be invoked.
567 : *
568 : * Drivers known to implement it: MEM, Zarr
569 : *
570 : * This is the same as the C function GDALGroupDeleteGroup().
571 : *
572 : * @param osName Sub-group name.
573 : * @param papszOptions Driver specific options determining how the group.
574 : * should be deleted.
575 : *
576 : * @return true in case of success
577 : * @since GDAL 3.8
578 : */
579 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
580 : CPL_UNUSED CSLConstList papszOptions)
581 : {
582 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
583 0 : return false;
584 : }
585 :
586 : /************************************************************************/
587 : /* CreateDimension() */
588 : /************************************************************************/
589 :
590 : /** Create a dimension within a group.
591 : *
592 : * @note Driver implementation: drivers supporting CreateDimension() should
593 : * implement this method, but do not have necessarily to implement
594 : * GDALGroup::GetDimensions().
595 : *
596 : * Drivers known to implement it: MEM, netCDF
597 : *
598 : * This is the same as the C function GDALGroupCreateDimension().
599 : *
600 : * @param osName Dimension name.
601 : * @param osType Dimension type (might be empty, and ignored by drivers)
602 : * @param osDirection Dimension direction (might be empty, and ignored by
603 : * drivers)
604 : * @param nSize Number of values indexed by this dimension. Should be > 0.
605 : * @param papszOptions Driver specific options determining how the dimension
606 : * should be created.
607 : *
608 : * @return the new dimension, or nullptr if case of error
609 : */
610 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
611 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
612 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
613 : CPL_UNUSED CSLConstList papszOptions)
614 : {
615 0 : CPLError(CE_Failure, CPLE_NotSupported,
616 : "CreateDimension() not implemented");
617 0 : return nullptr;
618 : }
619 :
620 : /************************************************************************/
621 : /* CreateMDArray() */
622 : /************************************************************************/
623 :
624 : /** Create a multidimensional array within a group.
625 : *
626 : * It is recommended that the GDALDimension objects passed in aoDimensions
627 : * belong to this group, either by retrieving them with GetDimensions()
628 : * or creating a new one with CreateDimension().
629 : *
630 : * Optionally implemented.
631 : *
632 : * Drivers known to implement it: MEM, netCDF
633 : *
634 : * This is the same as the C function GDALGroupCreateMDArray().
635 : *
636 : * @note Driver implementation: drivers should take into account the possibility
637 : * that GDALDimension object passed in aoDimensions might belong to a different
638 : * group / dataset / driver and act accordingly.
639 : *
640 : * @param osName Array name.
641 : * @param aoDimensions List of dimensions, ordered from the slowest varying
642 : * dimension first to the fastest varying dimension last.
643 : * Might be empty for a scalar array (if supported by
644 : * driver)
645 : * @param oDataType Array data type.
646 : * @param papszOptions Driver specific options determining how the array
647 : * should be created.
648 : *
649 : * @return the new array, or nullptr in case of error
650 : */
651 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
652 : CPL_UNUSED const std::string &osName,
653 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
654 : CPL_UNUSED const GDALExtendedDataType &oDataType,
655 : CPL_UNUSED CSLConstList papszOptions)
656 : {
657 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
658 0 : return nullptr;
659 : }
660 :
661 : /************************************************************************/
662 : /* DeleteMDArray() */
663 : /************************************************************************/
664 :
665 : /** Delete an array from a group.
666 : *
667 : * Optionally implemented.
668 : *
669 : * After this call, if a previously obtained instance of the deleted object
670 : * is still alive, no method other than for freeing it should be invoked.
671 : *
672 : * Drivers known to implement it: MEM, Zarr
673 : *
674 : * This is the same as the C function GDALGroupDeleteMDArray().
675 : *
676 : * @param osName Arrayname.
677 : * @param papszOptions Driver specific options determining how the array.
678 : * should be deleted.
679 : *
680 : * @return true in case of success
681 : * @since GDAL 3.8
682 : */
683 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
684 : CPL_UNUSED CSLConstList papszOptions)
685 : {
686 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
687 0 : return false;
688 : }
689 :
690 : /************************************************************************/
691 : /* GetTotalCopyCost() */
692 : /************************************************************************/
693 :
694 : /** Return a total "cost" to copy the group.
695 : *
696 : * Used as a parameter for CopFrom()
697 : */
698 36 : GUInt64 GDALGroup::GetTotalCopyCost() const
699 : {
700 36 : GUInt64 nCost = COPY_COST;
701 36 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
702 :
703 72 : auto groupNames = GetGroupNames();
704 42 : for (const auto &name : groupNames)
705 : {
706 12 : auto subGroup = OpenGroup(name);
707 6 : if (subGroup)
708 : {
709 6 : nCost += subGroup->GetTotalCopyCost();
710 : }
711 : }
712 :
713 36 : auto arrayNames = GetMDArrayNames();
714 114 : for (const auto &name : arrayNames)
715 : {
716 156 : auto array = OpenMDArray(name);
717 78 : if (array)
718 : {
719 78 : nCost += array->GetTotalCopyCost();
720 : }
721 : }
722 72 : return nCost;
723 : }
724 :
725 : /************************************************************************/
726 : /* CopyFrom() */
727 : /************************************************************************/
728 :
729 : /** Copy the content of a group into a new (generally empty) group.
730 : *
731 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
732 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
733 : * of some output drivers this is not recommended)
734 : * @param poSrcGroup Source group. Must NOT be nullptr.
735 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
736 : * stop the copy. In relaxed mode, the copy will be attempted to
737 : * be pursued.
738 : * @param nCurCost Should be provided as a variable initially set to 0.
739 : * @param nTotalCost Total cost from GetTotalCopyCost().
740 : * @param pfnProgress Progress callback, or nullptr.
741 : * @param pProgressData Progress user data, or nullptr.
742 : * @param papszOptions Creation options. Currently, only array creation
743 : * options are supported. They must be prefixed with
744 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
745 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
746 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
747 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
748 : * Restriction to arrays of a given name is done with adding
749 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
750 : * a full qualified name.
751 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
752 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
753 : * scaled to UInt16 with scale and offset values being computed from the minimum
754 : * and maximum of the source array. The integer data type used can be set with
755 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
756 : *
757 : * @return true in case of success (or partial success if bStrict == false).
758 : */
759 36 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
760 : GDALDataset *poSrcDS,
761 : const std::shared_ptr<GDALGroup> &poSrcGroup,
762 : bool bStrict, GUInt64 &nCurCost,
763 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
764 : void *pProgressData, CSLConstList papszOptions)
765 : {
766 36 : if (pfnProgress == nullptr)
767 0 : pfnProgress = GDALDummyProgress;
768 :
769 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
770 : if (!(x)) \
771 : { \
772 : if (bStrict) \
773 : return false; \
774 : continue; \
775 : } \
776 : (void)0
777 :
778 : try
779 : {
780 36 : nCurCost += GDALGroup::COPY_COST;
781 :
782 72 : const auto srcDims = poSrcGroup->GetDimensions();
783 : std::map<std::string, std::shared_ptr<GDALDimension>>
784 72 : mapExistingDstDims;
785 72 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
786 96 : for (const auto &dim : srcDims)
787 : {
788 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
789 60 : dim->GetDirection(), dim->GetSize());
790 60 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
791 60 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
792 120 : auto poIndexingVarSrc(dim->GetIndexingVariable());
793 60 : if (poIndexingVarSrc)
794 : {
795 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
796 41 : ->GetName()] =
797 82 : dim->GetName();
798 : }
799 : }
800 :
801 72 : auto attrs = poSrcGroup->GetAttributes();
802 62 : for (const auto &attr : attrs)
803 : {
804 : auto dstAttr =
805 26 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
806 52 : attr->GetDataType());
807 26 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
808 26 : auto raw(attr->ReadAsRaw());
809 26 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
810 0 : return false;
811 : }
812 36 : if (!attrs.empty())
813 : {
814 10 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
815 10 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
816 0 : return false;
817 : }
818 :
819 : const auto CopyArray =
820 78 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
821 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
822 : papszOptions, bStrict, &nCurCost,
823 694 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
824 : {
825 : // Map source dimensions to target dimensions
826 156 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
827 78 : const auto &srcArrayDims(srcArray->GetDimensions());
828 193 : for (const auto &dim : srcArrayDims)
829 : {
830 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
831 115 : dim->GetFullName());
832 115 : if (dstDim && dstDim->GetSize() == dim->GetSize())
833 : {
834 105 : dstArrayDims.emplace_back(dstDim);
835 : }
836 : else
837 : {
838 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
839 19 : if (oIter != mapExistingDstDims.end() &&
840 9 : oIter->second->GetSize() == dim->GetSize())
841 : {
842 8 : dstArrayDims.emplace_back(oIter->second);
843 : }
844 : else
845 : {
846 2 : std::string newDimName;
847 2 : if (oIter == mapExistingDstDims.end())
848 : {
849 1 : newDimName = dim->GetName();
850 : }
851 : else
852 : {
853 1 : std::string newDimNamePrefix(srcArray->GetName() +
854 3 : '_' + dim->GetName());
855 1 : newDimName = newDimNamePrefix;
856 1 : int nIterCount = 2;
857 0 : while (
858 1 : cpl::contains(mapExistingDstDims, newDimName))
859 : {
860 0 : newDimName = newDimNamePrefix +
861 0 : CPLSPrintf("_%d", nIterCount);
862 0 : nIterCount++;
863 : }
864 : }
865 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
866 : dim->GetDirection(),
867 4 : dim->GetSize());
868 2 : if (!dstDim)
869 0 : return false;
870 2 : mapExistingDstDims[newDimName] = dstDim;
871 2 : dstArrayDims.emplace_back(dstDim);
872 : }
873 : }
874 : }
875 :
876 156 : CPLStringList aosArrayCO;
877 78 : bool bAutoScale = false;
878 78 : GDALDataType eAutoScaleType = GDT_UInt16;
879 85 : for (const char *pszItem : cpl::Iterate(papszOptions))
880 : {
881 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
882 : {
883 7 : const char *pszOption = pszItem + strlen("ARRAY:");
884 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
885 : {
886 1 : const char *pszNext = strchr(pszOption, ':');
887 1 : if (pszNext != nullptr)
888 : {
889 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
890 1 : if (static_cast<size_t>(nDim) ==
891 1 : dstArrayDims.size())
892 : {
893 1 : pszOption = pszNext + 1;
894 : }
895 : else
896 : {
897 0 : pszOption = nullptr;
898 : }
899 : }
900 : }
901 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
902 : {
903 2 : const char *pszName = pszOption + strlen("IF(NAME=");
904 2 : const char *pszNext = strchr(pszName, ':');
905 2 : if (pszNext != nullptr && pszNext > pszName &&
906 2 : pszNext[-1] == ')')
907 : {
908 4 : CPLString osName;
909 2 : osName.assign(pszName, pszNext - pszName - 1);
910 3 : if (osName == srcArray->GetName() ||
911 1 : osName == srcArray->GetFullName())
912 : {
913 2 : pszOption = pszNext + 1;
914 : }
915 : else
916 : {
917 0 : pszOption = nullptr;
918 : }
919 : }
920 : }
921 7 : if (pszOption)
922 : {
923 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
924 : {
925 : bAutoScale =
926 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
927 : }
928 5 : else if (STARTS_WITH_CI(pszOption,
929 : "AUTOSCALE_DATA_TYPE="))
930 : {
931 1 : const char *pszDataType =
932 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
933 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
934 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
935 1 : GDALDataTypeIsFloating(eAutoScaleType))
936 : {
937 0 : CPLError(CE_Failure, CPLE_NotSupported,
938 : "Unsupported value for "
939 : "AUTOSCALE_DATA_TYPE");
940 0 : return false;
941 : }
942 : }
943 : else
944 : {
945 4 : aosArrayCO.AddString(pszOption);
946 : }
947 : }
948 : }
949 : }
950 :
951 78 : if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
952 : {
953 154 : const auto anBlockSize = srcArray->GetBlockSize();
954 154 : std::string osBlockSize;
955 83 : for (auto v : anBlockSize)
956 : {
957 78 : if (v == 0)
958 : {
959 72 : osBlockSize.clear();
960 72 : break;
961 : }
962 6 : if (!osBlockSize.empty())
963 2 : osBlockSize += ',';
964 6 : osBlockSize += std::to_string(v);
965 : }
966 77 : if (!osBlockSize.empty())
967 3 : aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
968 : }
969 :
970 : auto oIterDimName =
971 78 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
972 78 : const auto &srcArrayType = srcArray->GetDataType();
973 :
974 78 : std::shared_ptr<GDALMDArray> dstArray;
975 :
976 : // Only autoscale non-indexing variables
977 78 : bool bHasOffset = false;
978 78 : bool bHasScale = false;
979 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
980 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
981 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
982 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
983 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
984 82 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
985 80 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
986 : {
987 2 : constexpr bool bApproxOK = false;
988 2 : constexpr bool bForce = true;
989 2 : double dfMin = 0.0;
990 2 : double dfMax = 0.0;
991 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
992 : nullptr, nullptr, nullptr, nullptr,
993 2 : nullptr) != CE_None)
994 : {
995 0 : CPLError(CE_Failure, CPLE_AppDefined,
996 : "Could not retrieve statistics for array %s",
997 0 : srcArray->GetName().c_str());
998 0 : return false;
999 : }
1000 2 : double dfDTMin = 0;
1001 2 : double dfDTMax = 0;
1002 : #define setDTMinMax(ctype) \
1003 : do \
1004 : { \
1005 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1006 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1007 : } while (0)
1008 :
1009 2 : switch (eAutoScaleType)
1010 : {
1011 0 : case GDT_UInt8:
1012 0 : setDTMinMax(GByte);
1013 0 : break;
1014 0 : case GDT_Int8:
1015 0 : setDTMinMax(GInt8);
1016 0 : break;
1017 1 : case GDT_UInt16:
1018 1 : setDTMinMax(GUInt16);
1019 1 : break;
1020 1 : case GDT_Int16:
1021 1 : setDTMinMax(GInt16);
1022 1 : break;
1023 0 : case GDT_UInt32:
1024 0 : setDTMinMax(GUInt32);
1025 0 : break;
1026 0 : case GDT_Int32:
1027 0 : setDTMinMax(GInt32);
1028 0 : break;
1029 0 : case GDT_UInt64:
1030 0 : setDTMinMax(std::uint64_t);
1031 0 : break;
1032 0 : case GDT_Int64:
1033 0 : setDTMinMax(std::int64_t);
1034 0 : break;
1035 0 : case GDT_Float16:
1036 : case GDT_Float32:
1037 : case GDT_Float64:
1038 : case GDT_Unknown:
1039 : case GDT_CInt16:
1040 : case GDT_CInt32:
1041 : case GDT_CFloat16:
1042 : case GDT_CFloat32:
1043 : case GDT_CFloat64:
1044 : case GDT_TypeCount:
1045 0 : CPLAssert(false);
1046 : }
1047 :
1048 : dstArray =
1049 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1050 4 : GDALExtendedDataType::Create(eAutoScaleType),
1051 4 : aosArrayCO.List());
1052 2 : if (!dstArray)
1053 0 : return !bStrict;
1054 :
1055 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1056 : {
1057 : // If there's a nodata value in the source array, reserve
1058 : // DTMax for that purpose in the target scaled array
1059 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1060 : {
1061 0 : CPLError(CE_Failure, CPLE_AppDefined,
1062 : "Cannot set nodata value");
1063 0 : return false;
1064 : }
1065 1 : dfDTMax--;
1066 : }
1067 2 : const double dfScale =
1068 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1069 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1070 :
1071 4 : if (!dstArray->SetOffset(dfOffset) ||
1072 2 : !dstArray->SetScale(dfScale))
1073 : {
1074 0 : CPLError(CE_Failure, CPLE_AppDefined,
1075 : "Cannot set scale/offset");
1076 0 : return false;
1077 : }
1078 :
1079 2 : auto poUnscaled = dstArray->GetUnscaled();
1080 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1081 : {
1082 1 : poUnscaled->SetNoDataValue(
1083 : srcArray->GetNoDataValueAsDouble());
1084 : }
1085 :
1086 : // Copy source array into unscaled array
1087 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1088 : nCurCost, nTotalCost, pfnProgress,
1089 2 : pProgressData))
1090 : {
1091 0 : return false;
1092 : }
1093 : }
1094 : else
1095 : {
1096 152 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1097 152 : srcArrayType, aosArrayCO.List());
1098 76 : if (!dstArray)
1099 0 : return !bStrict;
1100 :
1101 152 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1102 : nCurCost, nTotalCost, pfnProgress,
1103 76 : pProgressData))
1104 : {
1105 0 : return false;
1106 : }
1107 : }
1108 :
1109 : // If this array is the indexing variable of a dimension, link them
1110 : // together.
1111 78 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1112 : {
1113 : auto oCorrespondingDimIter =
1114 41 : mapExistingDstDims.find(oIterDimName->second);
1115 41 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1116 : {
1117 : CPLErrorStateBackuper oErrorStateBackuper(
1118 41 : CPLQuietErrorHandler);
1119 82 : oCorrespondingDimIter->second->SetIndexingVariable(
1120 41 : std::move(dstArray));
1121 : }
1122 : }
1123 :
1124 78 : return true;
1125 36 : };
1126 :
1127 72 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1128 :
1129 : // Start by copying arrays that are indexing variables of dimensions
1130 114 : for (const auto &name : arrayNames)
1131 : {
1132 78 : auto srcArray = poSrcGroup->OpenMDArray(name);
1133 78 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1134 :
1135 78 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1136 197 : srcArray->GetName()) &&
1137 119 : !OpenMDArray(name))
1138 : {
1139 41 : if (!CopyArray(srcArray))
1140 0 : return false;
1141 : }
1142 : }
1143 :
1144 : // Then copy regular arrays
1145 114 : for (const auto &name : arrayNames)
1146 : {
1147 78 : auto srcArray = poSrcGroup->OpenMDArray(name);
1148 78 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1149 :
1150 78 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1151 193 : srcArray->GetName()) &&
1152 115 : !OpenMDArray(name))
1153 : {
1154 37 : if (!CopyArray(srcArray))
1155 0 : return false;
1156 : }
1157 : }
1158 :
1159 72 : const auto groupNames = poSrcGroup->GetGroupNames();
1160 42 : for (const auto &name : groupNames)
1161 : {
1162 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1163 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1164 6 : auto dstSubGroup = CreateGroup(name);
1165 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1166 12 : if (!dstSubGroup->CopyFrom(
1167 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1168 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1169 0 : return false;
1170 : }
1171 :
1172 36 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1173 0 : return false;
1174 :
1175 36 : return true;
1176 : }
1177 0 : catch (const std::exception &e)
1178 : {
1179 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1180 0 : return false;
1181 : }
1182 : }
1183 :
1184 : /************************************************************************/
1185 : /* GetInnerMostGroup() */
1186 : /************************************************************************/
1187 :
1188 : //! @cond Doxygen_Suppress
1189 : const GDALGroup *
1190 2446 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1191 : std::shared_ptr<GDALGroup> &curGroupHolder,
1192 : std::string &osLastPart) const
1193 : {
1194 2446 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1195 16 : return nullptr;
1196 2430 : const GDALGroup *poCurGroup = this;
1197 : CPLStringList aosTokens(
1198 4860 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1199 2430 : if (aosTokens.size() == 0)
1200 : {
1201 : // "/" case: the root group itself is the innermost group
1202 2 : osLastPart.clear();
1203 2 : return poCurGroup;
1204 : }
1205 :
1206 2977 : for (int i = 0; i < aosTokens.size() - 1; i++)
1207 : {
1208 565 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1209 565 : if (!curGroupHolder)
1210 : {
1211 16 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1212 : aosTokens[i]);
1213 16 : return nullptr;
1214 : }
1215 549 : poCurGroup = curGroupHolder.get();
1216 : }
1217 2412 : osLastPart = aosTokens[aosTokens.size() - 1];
1218 2412 : return poCurGroup;
1219 : }
1220 :
1221 : //! @endcond
1222 :
1223 : /************************************************************************/
1224 : /* OpenMDArrayFromFullname() */
1225 : /************************************************************************/
1226 :
1227 : /** Get an array from its fully qualified name */
1228 : std::shared_ptr<GDALMDArray>
1229 938 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1230 : CSLConstList papszOptions) const
1231 : {
1232 1876 : std::string osName;
1233 938 : std::shared_ptr<GDALGroup> curGroupHolder;
1234 938 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1235 938 : if (poGroup == nullptr)
1236 24 : return nullptr;
1237 914 : return poGroup->OpenMDArray(osName, papszOptions);
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* OpenAttributeFromFullname() */
1242 : /************************************************************************/
1243 :
1244 : /** Get an attribute from its fully qualified name */
1245 : std::shared_ptr<GDALAttribute>
1246 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1247 : CSLConstList papszOptions) const
1248 : {
1249 9 : const auto pos = osFullName.rfind('/');
1250 9 : if (pos == std::string::npos)
1251 0 : return nullptr;
1252 18 : const std::string attrName = osFullName.substr(pos + 1);
1253 9 : if (pos == 0)
1254 2 : return GetAttribute(attrName);
1255 14 : const std::string container = osFullName.substr(0, pos);
1256 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1257 7 : if (poArray)
1258 4 : return poArray->GetAttribute(attrName);
1259 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1260 3 : if (poGroup)
1261 1 : return poGroup->GetAttribute(attrName);
1262 2 : return nullptr;
1263 : }
1264 :
1265 : /************************************************************************/
1266 : /* ResolveMDArray() */
1267 : /************************************************************************/
1268 :
1269 : /** Locate an array in a group and its subgroups by name.
1270 : *
1271 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1272 : * used
1273 : * Otherwise the search will start from the group identified by osStartingPath,
1274 : * and an array whose name is osName will be looked for in this group (if
1275 : * osStartingPath is empty or "/", then the current group is used). If there
1276 : * is no match, then a recursive descendant search will be made in its
1277 : * subgroups. If there is no match in the subgroups, then the parent (if
1278 : * existing) of the group pointed by osStartingPath will be used as the new
1279 : * starting point for the search.
1280 : *
1281 : * @param osName name, qualified or not
1282 : * @param osStartingPath fully qualified name of the (sub-)group from which
1283 : * the search should be started. If this is a non-empty
1284 : * string, the group on which this method is called should
1285 : * nominally be the root group (otherwise the path will
1286 : * be interpreted as from the current group)
1287 : * @param papszOptions options to pass to OpenMDArray()
1288 : * @since GDAL 3.2
1289 : */
1290 : std::shared_ptr<GDALMDArray>
1291 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1292 : const std::string &osStartingPath,
1293 : CSLConstList papszOptions) const
1294 : {
1295 19 : if (!osName.empty() && osName[0] == '/')
1296 : {
1297 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1298 1 : if (poArray)
1299 1 : return poArray;
1300 : }
1301 36 : std::string osPath(osStartingPath);
1302 36 : std::set<std::string> oSetAlreadyVisited;
1303 :
1304 : while (true)
1305 : {
1306 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1307 0 : std::shared_ptr<GDALGroup> poGroup;
1308 :
1309 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1310 22 : bool goOn = false;
1311 22 : if (osPath.empty() || osPath == "/")
1312 : {
1313 11 : goOn = true;
1314 : }
1315 : else
1316 : {
1317 22 : std::string osLastPart;
1318 : const GDALGroup *poGroupPtr =
1319 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1320 11 : if (poGroupPtr)
1321 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1322 22 : if (poGroup &&
1323 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1324 : {
1325 11 : oQueue.push(poGroup);
1326 11 : goOn = true;
1327 : }
1328 : }
1329 :
1330 22 : if (goOn)
1331 : {
1332 17 : do
1333 : {
1334 : const GDALGroup *groupPtr;
1335 39 : if (!oQueue.empty())
1336 : {
1337 28 : poGroup = oQueue.front();
1338 28 : oQueue.pop();
1339 28 : groupPtr = poGroup.get();
1340 : }
1341 : else
1342 : {
1343 11 : groupPtr = this;
1344 : }
1345 :
1346 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1347 39 : if (poArray)
1348 16 : return poArray;
1349 :
1350 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1351 47 : for (const auto &osGroupName : aosGroupNames)
1352 : {
1353 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1354 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1355 48 : poSubGroup->GetFullName()))
1356 : {
1357 24 : oQueue.push(poSubGroup);
1358 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1359 : }
1360 : }
1361 23 : } while (!oQueue.empty());
1362 : }
1363 :
1364 6 : if (osPath.empty() || osPath == "/")
1365 2 : break;
1366 :
1367 4 : const auto nPos = osPath.rfind('/');
1368 4 : if (nPos == 0)
1369 1 : osPath = "/";
1370 : else
1371 : {
1372 3 : if (nPos == std::string::npos)
1373 0 : break;
1374 3 : osPath.resize(nPos);
1375 : }
1376 4 : }
1377 2 : return nullptr;
1378 : }
1379 :
1380 : /************************************************************************/
1381 : /* OpenGroupFromFullname() */
1382 : /************************************************************************/
1383 :
1384 : /** Get a group from its fully qualified name.
1385 : * @since GDAL 3.2
1386 : */
1387 : std::shared_ptr<GDALGroup>
1388 1273 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1389 : CSLConstList papszOptions) const
1390 : {
1391 2546 : std::string osName;
1392 1273 : std::shared_ptr<GDALGroup> curGroupHolder;
1393 1273 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1394 1273 : if (poGroup == nullptr)
1395 4 : return nullptr;
1396 1269 : if (osName.empty())
1397 2 : return m_pSelf.lock();
1398 1267 : return poGroup->OpenGroup(osName, papszOptions);
1399 : }
1400 :
1401 : /************************************************************************/
1402 : /* OpenDimensionFromFullname() */
1403 : /************************************************************************/
1404 :
1405 : /** Get a dimension from its fully qualified name */
1406 : std::shared_ptr<GDALDimension>
1407 224 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1408 : {
1409 448 : std::string osName;
1410 224 : std::shared_ptr<GDALGroup> curGroupHolder;
1411 224 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1412 224 : if (poGroup == nullptr)
1413 4 : return nullptr;
1414 440 : auto dims(poGroup->GetDimensions());
1415 378 : for (auto &dim : dims)
1416 : {
1417 326 : if (dim->GetName() == osName)
1418 168 : return dim;
1419 : }
1420 52 : return nullptr;
1421 : }
1422 :
1423 : /************************************************************************/
1424 : /* ClearStatistics() */
1425 : /************************************************************************/
1426 :
1427 : /**
1428 : * \brief Clear statistics.
1429 : *
1430 : * @since GDAL 3.4
1431 : */
1432 0 : void GDALGroup::ClearStatistics()
1433 : {
1434 0 : auto groupNames = GetGroupNames();
1435 0 : for (const auto &name : groupNames)
1436 : {
1437 0 : auto subGroup = OpenGroup(name);
1438 0 : if (subGroup)
1439 : {
1440 0 : subGroup->ClearStatistics();
1441 : }
1442 : }
1443 :
1444 0 : auto arrayNames = GetMDArrayNames();
1445 0 : for (const auto &name : arrayNames)
1446 : {
1447 0 : auto array = OpenMDArray(name);
1448 0 : if (array)
1449 : {
1450 0 : array->ClearStatistics();
1451 : }
1452 : }
1453 0 : }
1454 :
1455 : /************************************************************************/
1456 : /* Rename() */
1457 : /************************************************************************/
1458 :
1459 : /** Rename the group.
1460 : *
1461 : * This is not implemented by all drivers.
1462 : *
1463 : * Drivers known to implement it: MEM, netCDF, ZARR.
1464 : *
1465 : * This is the same as the C function GDALGroupRename().
1466 : *
1467 : * @param osNewName New name.
1468 : *
1469 : * @return true in case of success
1470 : * @since GDAL 3.8
1471 : */
1472 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1473 : {
1474 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1475 0 : return false;
1476 : }
1477 :
1478 : /************************************************************************/
1479 : /* BaseRename() */
1480 : /************************************************************************/
1481 :
1482 : //! @cond Doxygen_Suppress
1483 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1484 : {
1485 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1486 8 : m_osFullName += osNewName;
1487 8 : m_osName = osNewName;
1488 :
1489 8 : NotifyChildrenOfRenaming();
1490 8 : }
1491 :
1492 : //! @endcond
1493 :
1494 : /************************************************************************/
1495 : /* ParentRenamed() */
1496 : /************************************************************************/
1497 :
1498 : //! @cond Doxygen_Suppress
1499 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1500 : {
1501 7 : m_osFullName = osNewParentFullName;
1502 7 : m_osFullName += "/";
1503 7 : m_osFullName += m_osName;
1504 :
1505 7 : NotifyChildrenOfRenaming();
1506 7 : }
1507 :
1508 : //! @endcond
1509 :
1510 : /************************************************************************/
1511 : /* Deleted() */
1512 : /************************************************************************/
1513 :
1514 : //! @cond Doxygen_Suppress
1515 28 : void GDALGroup::Deleted()
1516 : {
1517 28 : m_bValid = false;
1518 :
1519 28 : NotifyChildrenOfDeletion();
1520 28 : }
1521 :
1522 : //! @endcond
1523 :
1524 : /************************************************************************/
1525 : /* ParentDeleted() */
1526 : /************************************************************************/
1527 :
1528 : //! @cond Doxygen_Suppress
1529 3 : void GDALGroup::ParentDeleted()
1530 : {
1531 3 : Deleted();
1532 3 : }
1533 :
1534 : //! @endcond
1535 :
1536 : /************************************************************************/
1537 : /* CheckValidAndErrorOutIfNot() */
1538 : /************************************************************************/
1539 :
1540 : //! @cond Doxygen_Suppress
1541 41920 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1542 : {
1543 41920 : if (!m_bValid)
1544 : {
1545 14 : CPLError(CE_Failure, CPLE_AppDefined,
1546 : "This object has been deleted. No action on it is possible");
1547 : }
1548 41920 : return m_bValid;
1549 : }
1550 :
1551 : //! @endcond
1552 :
1553 : /************************************************************************/
1554 : /* RecursivelyVisitArrays() */
1555 : /************************************************************************/
1556 :
1557 : /** Recursively visit arrays of this group and its subgroups.
1558 : *
1559 : * @param visitor Callback function called on visited arrays.
1560 : *
1561 : * @since GDAL 3.14
1562 : */
1563 2 : void GDALGroup::RecursivelyVisitArrays(
1564 : std::function<void(const std::shared_ptr<GDALMDArray> &)> visitor)
1565 : {
1566 4 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1567 4 : auto poSelf = m_pSelf.lock();
1568 2 : if (poSelf)
1569 2 : oQueue.push(poSelf);
1570 6 : while (!oQueue.empty())
1571 : {
1572 8 : auto poGroup = oQueue.front();
1573 4 : oQueue.pop();
1574 :
1575 10 : for (const std::string &osName : poGroup->GetMDArrayNames())
1576 : {
1577 12 : auto poArray = poGroup->OpenMDArray(osName);
1578 6 : if (poArray)
1579 6 : visitor(poArray);
1580 : }
1581 :
1582 6 : for (const std::string &osName : poGroup->GetGroupNames())
1583 : {
1584 4 : auto poSubGroup = poGroup->OpenGroup(osName);
1585 2 : if (poSubGroup)
1586 2 : oQueue.push(poSubGroup);
1587 : }
1588 : }
1589 2 : }
|