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