Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: vrtmultidim.cpp
4 : * Purpose: Implementation of VRTDriver
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : /*! @cond Doxygen_Suppress */
14 :
15 : #include <algorithm>
16 : #include <limits>
17 : #include <mutex>
18 : #include <unordered_set>
19 : #include <utility>
20 :
21 : #include "cpl_mem_cache.h"
22 : #include "cpl_minixml.h"
23 : #include "cpl_multiproc.h"
24 : #include "vrtdataset.h"
25 :
26 : static std::shared_ptr<GDALMDArray> ParseArray(const CPLXMLNode *psTree,
27 : const char *pszVRTPath,
28 : const char *pszParentXMLNode);
29 :
30 : struct VRTArrayDatasetWrapper
31 : {
32 : VRTArrayDatasetWrapper(const VRTArrayDatasetWrapper &) = delete;
33 : VRTArrayDatasetWrapper &operator=(const VRTArrayDatasetWrapper &) = delete;
34 :
35 : GDALDataset *m_poDS;
36 :
37 9 : explicit VRTArrayDatasetWrapper(GDALDataset *poDS) : m_poDS(poDS)
38 : {
39 9 : CPLDebug("VRT", "Open %s", poDS->GetDescription());
40 9 : }
41 :
42 9 : ~VRTArrayDatasetWrapper()
43 9 : {
44 9 : CPLDebug("VRT", "Close %s", m_poDS->GetDescription());
45 9 : delete m_poDS;
46 9 : }
47 :
48 26 : GDALDataset *get() const
49 : {
50 26 : return m_poDS;
51 : }
52 : };
53 :
54 : typedef std::pair<std::shared_ptr<VRTArrayDatasetWrapper>,
55 : std::unordered_set<const void *>>
56 : CacheEntry;
57 : static std::mutex g_cacheLock;
58 : static lru11::Cache<std::string, CacheEntry> g_cacheSources(100);
59 :
60 : /************************************************************************/
61 : /* GetRootGroup() */
62 : /************************************************************************/
63 :
64 3886 : std::shared_ptr<GDALGroup> VRTDataset::GetRootGroup() const
65 : {
66 3886 : return m_poRootGroup;
67 : }
68 :
69 : /************************************************************************/
70 : /* VRTGroup() */
71 : /************************************************************************/
72 :
73 7 : VRTGroup::VRTGroup(const char *pszVRTPath)
74 14 : : GDALGroup(std::string(), std::string()),
75 21 : m_poRefSelf(std::make_shared<Ref>(this)), m_osVRTPath(pszVRTPath)
76 : {
77 7 : }
78 :
79 : /************************************************************************/
80 : /* VRTGroup() */
81 : /************************************************************************/
82 :
83 599 : VRTGroup::VRTGroup(const std::string &osParentName, const std::string &osName)
84 599 : : GDALGroup(osParentName, osName), m_poRefSelf(std::make_shared<Ref>(this))
85 : {
86 599 : }
87 :
88 : /************************************************************************/
89 : /* ~VRTGroup() */
90 : /************************************************************************/
91 :
92 1212 : VRTGroup::~VRTGroup()
93 : {
94 606 : if (m_poSharedRefRootGroup)
95 : {
96 336 : VRTGroup::Serialize();
97 : }
98 1212 : }
99 :
100 : /************************************************************************/
101 : /* SetIsRootGroup() */
102 : /************************************************************************/
103 :
104 336 : void VRTGroup::SetIsRootGroup()
105 : {
106 336 : m_poSharedRefRootGroup = std::make_shared<Ref>(this);
107 336 : }
108 :
109 : /************************************************************************/
110 : /* SetRootGroupRef() */
111 : /************************************************************************/
112 :
113 263 : void VRTGroup::SetRootGroupRef(const std::weak_ptr<Ref> &rgRef)
114 : {
115 263 : m_poWeakRefRootGroup = rgRef;
116 263 : }
117 :
118 : /************************************************************************/
119 : /* GetRootGroupRef() */
120 : /************************************************************************/
121 :
122 263 : std::weak_ptr<VRTGroup::Ref> VRTGroup::GetRootGroupRef() const
123 : {
124 263 : return m_poSharedRefRootGroup ? m_poSharedRefRootGroup
125 263 : : m_poWeakRefRootGroup;
126 : }
127 :
128 : /************************************************************************/
129 : /* GetRootGroup() */
130 : /************************************************************************/
131 :
132 3311 : VRTGroup *VRTGroup::GetRootGroup() const
133 : {
134 3311 : if (m_poSharedRefRootGroup)
135 2749 : return m_poSharedRefRootGroup->m_ptr;
136 562 : auto ref(m_poWeakRefRootGroup.lock());
137 562 : return ref ? ref->m_ptr : nullptr;
138 : }
139 :
140 : /************************************************************************/
141 : /* GetRootGroupSharedPtr() */
142 : /************************************************************************/
143 :
144 0 : std::shared_ptr<GDALGroup> VRTGroup::GetRootGroupSharedPtr() const
145 : {
146 0 : auto group = GetRootGroup();
147 0 : if (group)
148 0 : return group->m_pSelf.lock();
149 0 : return nullptr;
150 : }
151 :
152 : /************************************************************************/
153 : /* XMLInit() */
154 : /************************************************************************/
155 :
156 485 : bool VRTGroup::XMLInit(const std::shared_ptr<VRTGroup> &poRoot,
157 : const std::shared_ptr<VRTGroup> &poThisGroup,
158 : const CPLXMLNode *psNode, const char *pszVRTPath)
159 : {
160 485 : if (pszVRTPath != nullptr)
161 451 : m_osVRTPath = pszVRTPath;
162 :
163 2960 : for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
164 : {
165 2496 : if (psIter->eType == CXT_Element &&
166 2011 : strcmp(psIter->pszValue, "Group") == 0)
167 : {
168 : const char *pszSubGroupName =
169 255 : CPLGetXMLValue(psIter, "name", nullptr);
170 255 : if (pszSubGroupName == nullptr)
171 : {
172 1 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "Missing name attribute on Group");
174 1 : m_bDirty = false;
175 1 : return false;
176 : }
177 : auto poSubGroup(std::dynamic_pointer_cast<VRTGroup>(
178 508 : CreateGroup(pszSubGroupName)));
179 508 : if (poSubGroup == nullptr ||
180 254 : !poSubGroup->XMLInit(poRoot, poSubGroup, psIter,
181 : m_osVRTPath.c_str()))
182 : {
183 0 : m_bDirty = false;
184 0 : return false;
185 254 : }
186 : }
187 2241 : else if (psIter->eType == CXT_Element &&
188 1756 : strcmp(psIter->pszValue, "Dimension") == 0)
189 : {
190 : auto poDim = VRTDimension::Create(
191 605 : poThisGroup, poThisGroup->GetFullName(), psIter);
192 605 : if (!poDim)
193 : {
194 2 : m_bDirty = false;
195 2 : return false;
196 : }
197 1206 : m_oMapDimensions[poDim->GetName()] = poDim;
198 : }
199 1636 : else if (psIter->eType == CXT_Element &&
200 1151 : strcmp(psIter->pszValue, "Attribute") == 0)
201 : {
202 : auto poAttr =
203 132 : VRTAttribute::Create(poThisGroup->GetFullName(), psIter);
204 132 : if (!poAttr)
205 : {
206 3 : m_bDirty = false;
207 3 : return false;
208 : }
209 258 : m_oMapAttributes[poAttr->GetName()] = poAttr;
210 : }
211 1504 : else if (psIter->eType == CXT_Element &&
212 1019 : strcmp(psIter->pszValue, "Array") == 0)
213 : {
214 : auto poArray = VRTMDArray::Create(
215 1019 : poThisGroup, poThisGroup->GetFullName(), psIter);
216 1019 : if (!poArray)
217 : {
218 15 : m_bDirty = false;
219 15 : return false;
220 : }
221 1004 : m_oMapMDArrays[poArray->GetName()] = poArray;
222 : }
223 : }
224 :
225 464 : m_bDirty = false;
226 464 : return true;
227 : }
228 :
229 : /************************************************************************/
230 : /* Serialize() */
231 : /************************************************************************/
232 :
233 673 : bool VRTGroup::Serialize() const
234 : {
235 673 : if (!m_bDirty || m_osFilename.empty())
236 669 : return true;
237 4 : m_bDirty = false;
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Create the output file. */
241 : /* -------------------------------------------------------------------- */
242 4 : VSILFILE *fpVRT = VSIFOpenL(m_osFilename.c_str(), "w");
243 4 : if (fpVRT == nullptr)
244 : {
245 1 : CPLError(CE_Failure, CPLE_AppDefined,
246 : "Failed to write .vrt file in Serialize().");
247 1 : return false;
248 : }
249 :
250 3 : CPLXMLNode *psDSTree = SerializeToXML(m_osVRTPath.c_str());
251 3 : char *pszXML = CPLSerializeXMLTree(psDSTree);
252 :
253 3 : CPLDestroyXMLNode(psDSTree);
254 :
255 3 : bool bOK = true;
256 3 : if (pszXML)
257 : {
258 : /* ------------------------------------------------------------------ */
259 : /* Write to disk. */
260 : /* ------------------------------------------------------------------ */
261 3 : bOK &= VSIFWriteL(pszXML, 1, strlen(pszXML), fpVRT) == strlen(pszXML);
262 3 : CPLFree(pszXML);
263 : }
264 3 : if (VSIFCloseL(fpVRT) != 0)
265 0 : bOK = false;
266 3 : if (!bOK)
267 : {
268 0 : CPLError(CE_Failure, CPLE_AppDefined,
269 : "Failed to write .vrt file in Serialize().");
270 : }
271 3 : return bOK;
272 : }
273 :
274 : /************************************************************************/
275 : /* SerializeToXML() */
276 : /************************************************************************/
277 :
278 80 : CPLXMLNode *VRTGroup::SerializeToXML(const char *pszVRTPath) const
279 : {
280 80 : CPLXMLNode *psDSTree = CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset");
281 80 : Serialize(psDSTree, pszVRTPath);
282 80 : return psDSTree;
283 : }
284 :
285 : /************************************************************************/
286 : /* Serialize() */
287 : /************************************************************************/
288 :
289 100 : void VRTGroup::Serialize(CPLXMLNode *psParent, const char *pszVRTPath) const
290 : {
291 100 : CPLXMLNode *psGroup = CPLCreateXMLNode(psParent, CXT_Element, "Group");
292 100 : CPLAddXMLAttributeAndValue(psGroup, "name", GetName().c_str());
293 201 : for (const auto &iter : m_oMapDimensions)
294 : {
295 101 : iter.second->Serialize(psGroup);
296 : }
297 111 : for (const auto &iter : m_oMapAttributes)
298 : {
299 11 : iter.second->Serialize(psGroup);
300 : }
301 252 : for (const auto &iter : m_oMapMDArrays)
302 : {
303 152 : iter.second->Serialize(psGroup, pszVRTPath);
304 : }
305 120 : for (const auto &iter : m_oMapGroups)
306 : {
307 20 : iter.second->Serialize(psGroup, pszVRTPath);
308 : }
309 100 : }
310 :
311 : /************************************************************************/
312 : /* GetGroupNames() */
313 : /************************************************************************/
314 :
315 59 : std::vector<std::string> VRTGroup::GetGroupNames(CSLConstList) const
316 : {
317 59 : std::vector<std::string> names;
318 90 : for (const auto &iter : m_oMapGroups)
319 31 : names.push_back(iter.first);
320 59 : return names;
321 : }
322 :
323 : /************************************************************************/
324 : /* OpenGroupInternal() */
325 : /************************************************************************/
326 :
327 : std::shared_ptr<VRTGroup>
328 54 : VRTGroup::OpenGroupInternal(const std::string &osName) const
329 : {
330 54 : auto oIter = m_oMapGroups.find(osName);
331 54 : if (oIter != m_oMapGroups.end())
332 52 : return oIter->second;
333 2 : return nullptr;
334 : }
335 :
336 : /************************************************************************/
337 : /* GetDimensions() */
338 : /************************************************************************/
339 :
340 : std::vector<std::shared_ptr<GDALDimension>>
341 136 : VRTGroup::GetDimensions(CSLConstList) const
342 : {
343 136 : std::vector<std::shared_ptr<GDALDimension>> oRes;
344 359 : for (const auto &oIter : m_oMapDimensions)
345 : {
346 223 : oRes.push_back(oIter.second);
347 : }
348 136 : return oRes;
349 : }
350 :
351 : /************************************************************************/
352 : /* GetDimensionFromFullName() */
353 : /************************************************************************/
354 :
355 : std::shared_ptr<VRTDimension>
356 1975 : VRTGroup::GetDimensionFromFullName(const std::string &name,
357 : bool bEmitError) const
358 : {
359 1975 : if (name[0] != '/')
360 : {
361 2780 : auto poDim(GetDimension(name));
362 1390 : if (!poDim)
363 : {
364 2 : if (bEmitError)
365 : {
366 1 : CPLError(CE_Failure, CPLE_AppDefined,
367 : "Cannot find dimension %s in this group",
368 : name.c_str());
369 : }
370 2 : return nullptr;
371 : }
372 1388 : return poDim;
373 : }
374 : else
375 : {
376 585 : auto curGroup(GetRootGroup());
377 585 : if (curGroup == nullptr)
378 : {
379 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
380 0 : return nullptr;
381 : }
382 1170 : CPLStringList aosTokens(CSLTokenizeString2(name.c_str(), "/", 0));
383 595 : for (int i = 0; i < aosTokens.size() - 1; i++)
384 : {
385 11 : curGroup = curGroup->OpenGroupInternal(aosTokens[i]).get();
386 11 : if (!curGroup)
387 : {
388 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
389 : aosTokens[i]);
390 1 : return nullptr;
391 : }
392 : }
393 1752 : auto poDim(curGroup->GetDimension(aosTokens.back()));
394 584 : if (!poDim)
395 : {
396 1 : if (bEmitError)
397 : {
398 1 : CPLError(CE_Failure, CPLE_AppDefined,
399 : "Cannot find dimension %s", name.c_str());
400 : }
401 1 : return nullptr;
402 : }
403 583 : return poDim;
404 : }
405 : }
406 :
407 : /************************************************************************/
408 : /* GetAttributes() */
409 : /************************************************************************/
410 :
411 : std::vector<std::shared_ptr<GDALAttribute>>
412 147 : VRTGroup::GetAttributes(CSLConstList) const
413 : {
414 147 : std::vector<std::shared_ptr<GDALAttribute>> oRes;
415 172 : for (const auto &oIter : m_oMapAttributes)
416 : {
417 25 : oRes.push_back(oIter.second);
418 : }
419 147 : return oRes;
420 : }
421 :
422 : /************************************************************************/
423 : /* GetMDArrayNames() */
424 : /************************************************************************/
425 :
426 60 : std::vector<std::string> VRTGroup::GetMDArrayNames(CSLConstList) const
427 : {
428 60 : std::vector<std::string> names;
429 202 : for (const auto &iter : m_oMapMDArrays)
430 142 : names.push_back(iter.first);
431 60 : return names;
432 : }
433 :
434 : /************************************************************************/
435 : /* OpenMDArray() */
436 : /************************************************************************/
437 :
438 714 : std::shared_ptr<GDALMDArray> VRTGroup::OpenMDArray(const std::string &osName,
439 : CSLConstList) const
440 : {
441 714 : auto oIter = m_oMapMDArrays.find(osName);
442 714 : if (oIter != m_oMapMDArrays.end())
443 678 : return oIter->second;
444 36 : return nullptr;
445 : }
446 :
447 : /************************************************************************/
448 : /* SetDirty() */
449 : /************************************************************************/
450 :
451 2686 : void VRTGroup::SetDirty()
452 : {
453 2686 : auto poRootGroup(GetRootGroup());
454 2686 : if (poRootGroup)
455 2682 : poRootGroup->m_bDirty = true;
456 2686 : }
457 :
458 : /************************************************************************/
459 : /* CreateGroup() */
460 : /************************************************************************/
461 :
462 265 : std::shared_ptr<GDALGroup> VRTGroup::CreateGroup(const std::string &osName,
463 : CSLConstList /*papszOptions*/)
464 : {
465 265 : if (osName.empty())
466 : {
467 1 : CPLError(CE_Failure, CPLE_NotSupported,
468 : "Empty group name not supported");
469 1 : return nullptr;
470 : }
471 264 : if (m_oMapGroups.find(osName) != m_oMapGroups.end())
472 : {
473 1 : CPLError(CE_Failure, CPLE_AppDefined,
474 : "A group with same name (%s) already exists", osName.c_str());
475 1 : return nullptr;
476 : }
477 263 : SetDirty();
478 789 : auto newGroup(VRTGroup::Create(GetFullName(), osName.c_str()));
479 263 : newGroup->SetRootGroupRef(GetRootGroupRef());
480 263 : m_oMapGroups[osName] = newGroup;
481 263 : return newGroup;
482 : }
483 :
484 : /************************************************************************/
485 : /* CreateDimension() */
486 : /************************************************************************/
487 :
488 : std::shared_ptr<GDALDimension>
489 96 : VRTGroup::CreateDimension(const std::string &osName, const std::string &osType,
490 : const std::string &osDirection, GUInt64 nSize,
491 : CSLConstList)
492 : {
493 96 : if (osName.empty())
494 : {
495 1 : CPLError(CE_Failure, CPLE_NotSupported,
496 : "Empty dimension name not supported");
497 1 : return nullptr;
498 : }
499 95 : if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
500 : {
501 1 : CPLError(CE_Failure, CPLE_AppDefined,
502 : "A dimension with same name (%s) already exists",
503 : osName.c_str());
504 1 : return nullptr;
505 : }
506 94 : SetDirty();
507 94 : auto newDim(std::make_shared<VRTDimension>(GetRef(), GetFullName(), osName,
508 : osType, osDirection, nSize,
509 282 : std::string()));
510 94 : m_oMapDimensions[osName] = newDim;
511 94 : return newDim;
512 : }
513 :
514 : /************************************************************************/
515 : /* CreateAttribute() */
516 : /************************************************************************/
517 :
518 : std::shared_ptr<GDALAttribute>
519 13 : VRTGroup::CreateAttribute(const std::string &osName,
520 : const std::vector<GUInt64> &anDimensions,
521 : const GDALExtendedDataType &oDataType, CSLConstList)
522 : {
523 13 : if (!VRTAttribute::CreationCommonChecks(osName, anDimensions,
524 13 : m_oMapAttributes))
525 : {
526 4 : return nullptr;
527 : }
528 9 : SetDirty();
529 : auto newAttr(std::make_shared<VRTAttribute>(
530 18 : (GetFullName() == "/" ? "/" : GetFullName() + "/") + "_GLOBAL_", osName,
531 27 : anDimensions.empty() ? 0 : anDimensions[0], oDataType));
532 9 : m_oMapAttributes[osName] = newAttr;
533 9 : return newAttr;
534 : }
535 :
536 : /************************************************************************/
537 : /* CreateMDArray() */
538 : /************************************************************************/
539 :
540 133 : std::shared_ptr<GDALMDArray> VRTGroup::CreateMDArray(
541 : const std::string &osName,
542 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
543 : const GDALExtendedDataType &oType, CSLConstList)
544 : {
545 133 : if (osName.empty())
546 : {
547 1 : CPLError(CE_Failure, CPLE_NotSupported,
548 : "Empty array name not supported");
549 1 : return nullptr;
550 : }
551 132 : if (m_oMapMDArrays.find(osName) != m_oMapMDArrays.end())
552 : {
553 1 : CPLError(CE_Failure, CPLE_AppDefined,
554 : "An array with same name (%s) already exists", osName.c_str());
555 1 : return nullptr;
556 : }
557 276 : for (auto &poDim : aoDimensions)
558 : {
559 : auto poFoundDim(
560 146 : dynamic_cast<const VRTDimension *>(poDim.get())
561 : ? GetDimensionFromFullName(poDim->GetFullName(), false)
562 146 : : nullptr);
563 146 : if (poFoundDim == nullptr || poFoundDim->GetSize() != poDim->GetSize())
564 : {
565 1 : CPLError(CE_Failure, CPLE_AppDefined,
566 : "One input dimension is not a VRTDimension "
567 : "or a VRTDimension of this dataset");
568 1 : return nullptr;
569 : }
570 : }
571 130 : auto newArray(std::make_shared<VRTMDArray>(GetRef(), GetFullName(), osName,
572 260 : aoDimensions, oType));
573 130 : newArray->SetSelf(newArray);
574 130 : m_oMapMDArrays[osName] = newArray;
575 130 : return newArray;
576 : }
577 :
578 : /************************************************************************/
579 : /* ParseDataType() */
580 : /************************************************************************/
581 :
582 1195 : static GDALExtendedDataType ParseDataType(const CPLXMLNode *psNode)
583 : {
584 1195 : const auto *psType = CPLGetXMLNode(psNode, "DataType");
585 1195 : if (psType == nullptr || psType->psChild == nullptr ||
586 1193 : psType->psChild->eType != CXT_Text)
587 : {
588 2 : CPLError(CE_Failure, CPLE_AppDefined,
589 : "Unhandled content for DataType or Missing");
590 2 : return GDALExtendedDataType::Create(GDT_Unknown);
591 : }
592 2386 : GDALExtendedDataType dt(GDALExtendedDataType::CreateString());
593 1193 : if (EQUAL(psType->psChild->pszValue, "String"))
594 : {
595 : // done
596 : }
597 : else
598 : {
599 767 : const auto eDT = GDALGetDataTypeByName(psType->psChild->pszValue);
600 767 : dt = GDALExtendedDataType::Create(eDT);
601 : }
602 1193 : return dt;
603 : }
604 :
605 : /************************************************************************/
606 : /* Create() */
607 : /************************************************************************/
608 :
609 : std::shared_ptr<VRTDimension>
610 617 : VRTDimension::Create(const std::shared_ptr<VRTGroup> &poThisGroup,
611 : const std::string &osParentName, const CPLXMLNode *psNode)
612 : {
613 617 : const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
614 617 : if (pszName == nullptr)
615 : {
616 2 : CPLError(CE_Failure, CPLE_AppDefined,
617 : "Missing name attribute on Dimension");
618 2 : return nullptr;
619 : }
620 615 : const char *pszType = CPLGetXMLValue(psNode, "type", "");
621 615 : const char *pszDirection = CPLGetXMLValue(psNode, "direction", "");
622 615 : const char *pszSize = CPLGetXMLValue(psNode, "size", "");
623 : GUInt64 nSize = static_cast<GUInt64>(
624 615 : CPLScanUIntBig(pszSize, static_cast<int>(strlen(pszSize))));
625 615 : if (nSize == 0)
626 : {
627 1 : CPLError(CE_Failure, CPLE_AppDefined,
628 : "Invalid value for size attribute on Dimension");
629 1 : return nullptr;
630 : }
631 : const char *pszIndexingVariable =
632 614 : CPLGetXMLValue(psNode, "indexingVariable", "");
633 : return std::make_shared<VRTDimension>(poThisGroup->GetRef(), osParentName,
634 : pszName, pszType, pszDirection, nSize,
635 614 : pszIndexingVariable);
636 : }
637 :
638 : /************************************************************************/
639 : /* Serialize() */
640 : /************************************************************************/
641 :
642 102 : void VRTDimension::Serialize(CPLXMLNode *psParent) const
643 : {
644 : CPLXMLNode *psDimension =
645 102 : CPLCreateXMLNode(psParent, CXT_Element, "Dimension");
646 102 : CPLAddXMLAttributeAndValue(psDimension, "name", GetName().c_str());
647 102 : if (!m_osType.empty())
648 : {
649 91 : CPLAddXMLAttributeAndValue(psDimension, "type", m_osType.c_str());
650 : }
651 102 : if (!m_osDirection.empty())
652 : {
653 50 : CPLAddXMLAttributeAndValue(psDimension, "direction",
654 : m_osDirection.c_str());
655 : }
656 102 : CPLAddXMLAttributeAndValue(
657 : psDimension, "size",
658 102 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(m_nSize)));
659 102 : if (!m_osIndexingVariableName.empty())
660 : {
661 50 : CPLAddXMLAttributeAndValue(psDimension, "indexingVariable",
662 : m_osIndexingVariableName.c_str());
663 : }
664 102 : }
665 :
666 : /************************************************************************/
667 : /* GetGroup() */
668 : /************************************************************************/
669 :
670 683 : VRTGroup *VRTDimension::GetGroup() const
671 : {
672 683 : auto ref = m_poGroupRef.lock();
673 683 : return ref ? ref->m_ptr : nullptr;
674 : }
675 :
676 : /************************************************************************/
677 : /* GetIndexingVariable() */
678 : /************************************************************************/
679 :
680 250 : std::shared_ptr<GDALMDArray> VRTDimension::GetIndexingVariable() const
681 : {
682 250 : if (m_osIndexingVariableName.empty())
683 13 : return nullptr;
684 237 : auto poGroup = GetGroup();
685 237 : if (poGroup == nullptr)
686 : {
687 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot access group");
688 0 : return nullptr;
689 : }
690 237 : std::shared_ptr<GDALMDArray> poVar;
691 237 : if (m_osIndexingVariableName[0] != '/')
692 : {
693 236 : poVar = poGroup->OpenMDArray(m_osIndexingVariableName);
694 : }
695 : else
696 : {
697 1 : poGroup = poGroup->GetRootGroup();
698 1 : if (poGroup == nullptr)
699 : {
700 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
701 0 : return nullptr;
702 : }
703 1 : poVar = poGroup->OpenMDArrayFromFullname(m_osIndexingVariableName);
704 : }
705 237 : if (!poVar)
706 : {
707 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find variable %s",
708 : m_osIndexingVariableName.c_str());
709 : }
710 237 : return poVar;
711 : }
712 :
713 : /************************************************************************/
714 : /* SetIndexingVariable() */
715 : /************************************************************************/
716 :
717 39 : bool VRTDimension::SetIndexingVariable(
718 : std::shared_ptr<GDALMDArray> poIndexingVariable)
719 : {
720 39 : if (poIndexingVariable == nullptr)
721 : {
722 0 : m_osIndexingVariableName.clear();
723 0 : return true;
724 : }
725 :
726 39 : auto poGroup = GetGroup();
727 39 : if (poGroup == nullptr)
728 : {
729 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot access group");
730 0 : return false;
731 : }
732 39 : poGroup = poGroup->GetRootGroup();
733 39 : if (poGroup == nullptr)
734 : {
735 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
736 0 : return false;
737 : }
738 : auto poVar(std::dynamic_pointer_cast<VRTMDArray>(
739 78 : poGroup->OpenMDArrayFromFullname(poIndexingVariable->GetFullName())));
740 39 : if (!poVar)
741 : {
742 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find variable %s",
743 0 : poIndexingVariable->GetFullName().c_str());
744 0 : return false;
745 : }
746 39 : if (poVar->GetGroup() == GetGroup())
747 : {
748 39 : m_osIndexingVariableName = poIndexingVariable->GetName();
749 : }
750 : else
751 : {
752 0 : m_osIndexingVariableName = poIndexingVariable->GetFullName();
753 : }
754 39 : return true;
755 : }
756 :
757 : /************************************************************************/
758 : /* CreationCommonChecks() */
759 : /************************************************************************/
760 :
761 72 : bool VRTAttribute::CreationCommonChecks(
762 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
763 : const std::map<std::string, std::shared_ptr<VRTAttribute>> &oMapAttributes)
764 : {
765 72 : if (osName.empty())
766 : {
767 2 : CPLError(CE_Failure, CPLE_NotSupported,
768 : "Empty attribute name not supported");
769 2 : return false;
770 : }
771 70 : if (oMapAttributes.find(osName) != oMapAttributes.end())
772 : {
773 2 : CPLError(CE_Failure, CPLE_AppDefined,
774 : "An attribute with same name (%s) already exists",
775 : osName.c_str());
776 2 : return false;
777 : }
778 68 : if (anDimensions.size() >= 2)
779 : {
780 1 : CPLError(CE_Failure, CPLE_AppDefined,
781 : "Only single dimensional attribute handled");
782 1 : return false;
783 : }
784 72 : if (anDimensions.size() == 1 &&
785 5 : anDimensions[0] > static_cast<GUInt64>(INT_MAX))
786 : {
787 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too large attribute");
788 1 : return false;
789 : }
790 66 : return true;
791 : }
792 :
793 : /************************************************************************/
794 : /* Create() */
795 : /************************************************************************/
796 :
797 : std::shared_ptr<VRTAttribute>
798 176 : VRTAttribute::Create(const std::string &osParentName, const CPLXMLNode *psNode)
799 : {
800 176 : const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
801 176 : if (pszName == nullptr)
802 : {
803 1 : CPLError(CE_Failure, CPLE_AppDefined,
804 : "Missing name attribute on Attribute");
805 1 : return nullptr;
806 : }
807 350 : GDALExtendedDataType dt(ParseDataType(psNode));
808 210 : if (dt.GetClass() == GEDTC_NUMERIC &&
809 35 : dt.GetNumericDataType() == GDT_Unknown)
810 : {
811 2 : return nullptr;
812 : }
813 346 : std::vector<std::string> aosValues;
814 692 : for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
815 : {
816 519 : if (psIter->eType == CXT_Element &&
817 346 : strcmp(psIter->pszValue, "Value") == 0)
818 : {
819 173 : aosValues.push_back(CPLGetXMLValue(psIter, nullptr, ""));
820 : }
821 : }
822 : return std::make_shared<VRTAttribute>(osParentName, pszName, dt,
823 173 : std::move(aosValues));
824 : }
825 :
826 : /************************************************************************/
827 : /* IRead() */
828 : /************************************************************************/
829 :
830 32 : bool VRTAttribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
831 : const GInt64 *arrayStep,
832 : const GPtrDiff_t *bufferStride,
833 : const GDALExtendedDataType &bufferDataType,
834 : void *pDstBuffer) const
835 : {
836 32 : const auto stringDT(GDALExtendedDataType::CreateString());
837 32 : if (m_aosList.empty())
838 : {
839 1 : const char *pszStr = nullptr;
840 1 : GDALExtendedDataType::CopyValue(&pszStr, stringDT, pDstBuffer,
841 : bufferDataType);
842 : }
843 : else
844 : {
845 31 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
846 69 : for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
847 : {
848 : const int idx =
849 38 : m_dims.empty()
850 38 : ? 0
851 10 : : static_cast<int>(arrayStartIdx[0] + i * arrayStep[0]);
852 38 : const char *pszStr = m_aosList[idx].data();
853 38 : GDALExtendedDataType::CopyValue(&pszStr, stringDT, pabyDstBuffer,
854 : bufferDataType);
855 38 : if (!m_dims.empty())
856 : {
857 10 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
858 : }
859 : }
860 : }
861 64 : return true;
862 : }
863 :
864 : /************************************************************************/
865 : /* IWrite() */
866 : /************************************************************************/
867 :
868 64 : bool VRTAttribute::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
869 : const GInt64 *arrayStep,
870 : const GPtrDiff_t *bufferStride,
871 : const GDALExtendedDataType &bufferDataType,
872 : const void *pSrcBuffer)
873 : {
874 66 : m_aosList.resize(m_dims.empty() ? 1
875 2 : : static_cast<int>(m_dims[0]->GetSize()));
876 64 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
877 64 : const auto stringDT(GDALExtendedDataType::CreateString());
878 134 : for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
879 : {
880 : const int idx =
881 70 : m_dims.empty()
882 70 : ? 0
883 8 : : static_cast<int>(arrayStartIdx[0] + i * arrayStep[0]);
884 70 : char *pszStr = nullptr;
885 70 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType, &pszStr,
886 : stringDT);
887 70 : m_aosList[idx] = pszStr ? pszStr : "";
888 70 : CPLFree(pszStr);
889 70 : if (!m_dims.empty())
890 : {
891 8 : pabySrcBuffer += bufferStride[0] * bufferDataType.GetSize();
892 : }
893 : }
894 128 : return true;
895 : }
896 :
897 : /************************************************************************/
898 : /* Serialize() */
899 : /************************************************************************/
900 :
901 56 : void VRTAttribute::Serialize(CPLXMLNode *psParent) const
902 : {
903 56 : CPLXMLNode *psAttr = CPLCreateXMLNode(psParent, CXT_Element, "Attribute");
904 56 : CPLAddXMLAttributeAndValue(psAttr, "name", GetName().c_str());
905 56 : CPLXMLNode *psDataType = CPLCreateXMLNode(psAttr, CXT_Element, "DataType");
906 56 : if (m_dt.GetClass() == GEDTC_STRING)
907 25 : CPLCreateXMLNode(psDataType, CXT_Text, "String");
908 : else
909 31 : CPLCreateXMLNode(psDataType, CXT_Text,
910 : GDALGetDataTypeName(m_dt.GetNumericDataType()));
911 56 : CPLXMLNode *psLast = psDataType;
912 110 : for (const auto &str : m_aosList)
913 : {
914 54 : CPLXMLNode *psValue = CPLCreateXMLNode(nullptr, CXT_Element, "Value");
915 54 : CPLCreateXMLNode(psValue, CXT_Text, str.c_str());
916 54 : psLast->psNext = psValue;
917 54 : psLast = psValue;
918 : }
919 56 : }
920 :
921 : /************************************************************************/
922 : /* Create() */
923 : /************************************************************************/
924 :
925 : std::shared_ptr<VRTMDArray>
926 1021 : VRTMDArray::Create(const std::shared_ptr<VRTGroup> &poThisGroup,
927 : const std::string &osParentName, const CPLXMLNode *psNode)
928 : {
929 1021 : const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
930 1021 : if (pszName == nullptr)
931 : {
932 1 : CPLError(CE_Failure, CPLE_AppDefined,
933 : "Missing name attribute on Array");
934 1 : return nullptr;
935 : }
936 :
937 : /* -------------------------------------------------------------------- */
938 : /* Check for an SRS node. */
939 : /* -------------------------------------------------------------------- */
940 1020 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psNode, "SRS");
941 1020 : std::unique_ptr<OGRSpatialReference> poSRS;
942 1020 : if (psSRSNode)
943 : {
944 2 : poSRS = std::make_unique<OGRSpatialReference>();
945 2 : poSRS->SetFromUserInput(
946 : CPLGetXMLValue(psSRSNode, nullptr, ""),
947 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
948 : const char *pszMapping =
949 2 : CPLGetXMLValue(psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
950 2 : if (pszMapping)
951 : {
952 : char **papszTokens =
953 1 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
954 2 : std::vector<int> anMapping;
955 3 : for (int i = 0; papszTokens && papszTokens[i]; i++)
956 : {
957 2 : anMapping.push_back(atoi(papszTokens[i]));
958 : }
959 1 : CSLDestroy(papszTokens);
960 1 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
961 : }
962 : }
963 :
964 2040 : GDALExtendedDataType dt(ParseDataType(psNode));
965 1754 : if (dt.GetClass() == GEDTC_NUMERIC &&
966 734 : dt.GetNumericDataType() == GDT_Unknown)
967 : {
968 2 : return nullptr;
969 : }
970 2036 : std::vector<std::shared_ptr<GDALDimension>> dims;
971 2036 : std::map<std::string, std::shared_ptr<VRTAttribute>> oMapAttributes;
972 5767 : for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
973 : {
974 4753 : if (psIter->eType == CXT_Element &&
975 3729 : strcmp(psIter->pszValue, "Dimension") == 0)
976 : {
977 : auto poDim =
978 9 : VRTDimension::Create(poThisGroup, std::string(), psIter);
979 9 : if (!poDim)
980 0 : return nullptr;
981 18 : dims.emplace_back(poDim);
982 : }
983 4744 : else if (psIter->eType == CXT_Element &&
984 3720 : strcmp(psIter->pszValue, "DimensionRef") == 0)
985 : {
986 1645 : const char *pszRef = CPLGetXMLValue(psIter, "ref", nullptr);
987 1645 : if (pszRef == nullptr || pszRef[0] == '\0')
988 : {
989 1 : CPLError(CE_Failure, CPLE_AppDefined,
990 : "Missing ref attribute on DimensionRef");
991 4 : return nullptr;
992 : }
993 3288 : auto poDim(poThisGroup->GetDimensionFromFullName(pszRef, true));
994 1644 : if (!poDim)
995 3 : return nullptr;
996 3282 : dims.emplace_back(poDim);
997 : }
998 3099 : else if (psIter->eType == CXT_Element &&
999 2075 : strcmp(psIter->pszValue, "Attribute") == 0)
1000 : {
1001 : auto poAttr =
1002 88 : VRTAttribute::Create(osParentName + "/" + pszName, psIter);
1003 44 : if (!poAttr)
1004 0 : return nullptr;
1005 44 : oMapAttributes[poAttr->GetName()] = poAttr;
1006 : }
1007 : }
1008 :
1009 : auto array(std::make_shared<VRTMDArray>(poThisGroup->GetRef(), osParentName,
1010 1014 : pszName, dt, std::move(dims),
1011 3042 : std::move(oMapAttributes)));
1012 1014 : array->SetSelf(array);
1013 1014 : array->SetSpatialRef(poSRS.get());
1014 :
1015 1014 : const char *pszNoDataValue = CPLGetXMLValue(psNode, "NoDataValue", nullptr);
1016 1014 : if (pszNoDataValue)
1017 2 : array->SetNoDataValue(CPLAtof(pszNoDataValue));
1018 :
1019 1014 : const char *pszUnit = CPLGetXMLValue(psNode, "Unit", nullptr);
1020 1014 : if (pszUnit)
1021 1 : array->SetUnit(pszUnit);
1022 :
1023 1014 : const char *pszOffset = CPLGetXMLValue(psNode, "Offset", nullptr);
1024 1014 : if (pszOffset)
1025 1 : array->SetOffset(CPLAtof(pszOffset));
1026 :
1027 1014 : const char *pszScale = CPLGetXMLValue(psNode, "Scale", nullptr);
1028 1014 : if (pszScale)
1029 1 : array->SetScale(CPLAtof(pszScale));
1030 :
1031 5747 : for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
1032 : {
1033 4741 : if (psIter->eType == CXT_Element &&
1034 3721 : strcmp(psIter->pszValue, "RegularlySpacedValues") == 0)
1035 : {
1036 248 : if (dt.GetClass() != GEDTC_NUMERIC)
1037 : {
1038 0 : CPLError(CE_Failure, CPLE_AppDefined,
1039 : "RegularlySpacedValues only supported for numeric "
1040 : "data types");
1041 2 : return nullptr;
1042 : }
1043 248 : if (array->GetDimensionCount() != 1)
1044 : {
1045 0 : CPLError(CE_Failure, CPLE_AppDefined,
1046 : "RegularlySpacedValues only supported with single "
1047 : "dimension array");
1048 0 : return nullptr;
1049 : }
1050 248 : const char *pszStart = CPLGetXMLValue(psIter, "start", nullptr);
1051 248 : if (pszStart == nullptr)
1052 : {
1053 1 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "start attribute missing");
1055 1 : return nullptr;
1056 : }
1057 : const char *pszIncrement =
1058 247 : CPLGetXMLValue(psIter, "increment", nullptr);
1059 247 : if (pszIncrement == nullptr)
1060 : {
1061 1 : CPLError(CE_Failure, CPLE_AppDefined,
1062 : "increment attribute missing");
1063 1 : return nullptr;
1064 : }
1065 : std::unique_ptr<VRTMDArraySourceRegularlySpaced> poSource(
1066 246 : new VRTMDArraySourceRegularlySpaced(CPLAtof(pszStart),
1067 246 : CPLAtof(pszIncrement)));
1068 246 : array->AddSource(std::move(poSource));
1069 : }
1070 4493 : else if (psIter->eType == CXT_Element &&
1071 3473 : (strcmp(psIter->pszValue, "InlineValues") == 0 ||
1072 3466 : strcmp(psIter->pszValue, "InlineValuesWithValueElement") ==
1073 3222 : 0 ||
1074 3222 : strcmp(psIter->pszValue, "ConstantValue") == 0))
1075 : {
1076 : auto poSource(
1077 625 : VRTMDArraySourceInlinedValues::Create(array.get(), psIter));
1078 625 : if (!poSource)
1079 6 : return nullptr;
1080 1238 : array->AddSource(std::move(poSource));
1081 : }
1082 3868 : else if (psIter->eType == CXT_Element &&
1083 2848 : strcmp(psIter->pszValue, "Source") == 0)
1084 : {
1085 : auto poSource(
1086 133 : VRTMDArraySourceFromArray::Create(array.get(), psIter));
1087 133 : if (!poSource)
1088 0 : return nullptr;
1089 133 : array->AddSource(std::move(poSource));
1090 : }
1091 : }
1092 :
1093 1006 : return array;
1094 : }
1095 :
1096 : /************************************************************************/
1097 : /* Create() */
1098 : /************************************************************************/
1099 :
1100 2 : std::shared_ptr<VRTMDArray> VRTMDArray::Create(const char *pszVRTPath,
1101 : const CPLXMLNode *psNode)
1102 : {
1103 : auto poDummyGroup =
1104 4 : std::shared_ptr<VRTGroup>(new VRTGroup(pszVRTPath ? pszVRTPath : ""));
1105 2 : auto poArray = Create(poDummyGroup, std::string(), psNode);
1106 2 : if (poArray)
1107 2 : poArray->m_poDummyOwningGroup = std::move(poDummyGroup);
1108 4 : return poArray;
1109 : }
1110 :
1111 : /************************************************************************/
1112 : /* GetAttributes() */
1113 : /************************************************************************/
1114 :
1115 : std::vector<std::shared_ptr<GDALAttribute>>
1116 269 : VRTMDArray::GetAttributes(CSLConstList) const
1117 : {
1118 269 : std::vector<std::shared_ptr<GDALAttribute>> oRes;
1119 294 : for (const auto &oIter : m_oMapAttributes)
1120 : {
1121 25 : oRes.push_back(oIter.second);
1122 : }
1123 269 : return oRes;
1124 : }
1125 :
1126 : /************************************************************************/
1127 : /* Read() */
1128 : /************************************************************************/
1129 :
1130 66 : bool VRTMDArraySourceRegularlySpaced::Read(
1131 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
1132 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
1133 : void *pDstBuffer) const
1134 : {
1135 66 : GDALExtendedDataType dtFloat64(GDALExtendedDataType::Create(GDT_Float64));
1136 66 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
1137 577 : for (size_t i = 0; i < count[0]; i++)
1138 : {
1139 511 : const double dfVal =
1140 511 : m_dfStart + (arrayStartIdx[0] + i * arrayStep[0]) * m_dfIncrement;
1141 511 : GDALExtendedDataType::CopyValue(&dfVal, dtFloat64, pabyDstBuffer,
1142 : bufferDataType);
1143 511 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
1144 : }
1145 132 : return true;
1146 : }
1147 :
1148 : /************************************************************************/
1149 : /* Serialize() */
1150 : /************************************************************************/
1151 :
1152 14 : void VRTMDArraySourceRegularlySpaced::Serialize(CPLXMLNode *psParent,
1153 : const char *) const
1154 : {
1155 : CPLXMLNode *psSource =
1156 14 : CPLCreateXMLNode(psParent, CXT_Element, "RegularlySpacedValues");
1157 14 : CPLAddXMLAttributeAndValue(psSource, "start",
1158 14 : CPLSPrintf("%.17g", m_dfStart));
1159 14 : CPLAddXMLAttributeAndValue(psSource, "increment",
1160 14 : CPLSPrintf("%.17g", m_dfIncrement));
1161 14 : }
1162 :
1163 : /************************************************************************/
1164 : /* Create() */
1165 : /************************************************************************/
1166 :
1167 : std::unique_ptr<VRTMDArraySourceInlinedValues>
1168 625 : VRTMDArraySourceInlinedValues::Create(const VRTMDArray *array,
1169 : const CPLXMLNode *psNode)
1170 : {
1171 625 : const bool bIsConstantValue =
1172 625 : strcmp(psNode->pszValue, "ConstantValue") == 0;
1173 625 : const auto &dt(array->GetDataType());
1174 625 : const size_t nDTSize = dt.GetSize();
1175 625 : if (nDTSize == 0)
1176 0 : return nullptr;
1177 625 : if (strcmp(psNode->pszValue, "InlineValuesWithValueElement") == 0)
1178 : {
1179 244 : if (dt.GetClass() != GEDTC_NUMERIC && dt.GetClass() != GEDTC_STRING)
1180 : {
1181 0 : CPLError(CE_Failure, CPLE_AppDefined,
1182 : "Only numeric or string data type handled for "
1183 : "InlineValuesWithValueElement");
1184 0 : return nullptr;
1185 : }
1186 : }
1187 381 : else if (dt.GetClass() != GEDTC_NUMERIC)
1188 : {
1189 0 : CPLError(CE_Failure, CPLE_AppDefined,
1190 : "Only numeric data type handled for InlineValues");
1191 0 : return nullptr;
1192 : }
1193 :
1194 625 : const int nDimCount = static_cast<int>(array->GetDimensionCount());
1195 1250 : std::vector<GUInt64> anOffset(nDimCount);
1196 1250 : std::vector<size_t> anCount(nDimCount);
1197 625 : size_t nArrayByteSize = nDTSize;
1198 625 : if (nDimCount > 0)
1199 : {
1200 622 : const auto &dims(array->GetDimensions());
1201 :
1202 622 : const char *pszOffset = CPLGetXMLValue(psNode, "offset", nullptr);
1203 622 : if (pszOffset != nullptr)
1204 : {
1205 : CPLStringList aosTokensOffset(
1206 44 : CSLTokenizeString2(pszOffset, ", ", 0));
1207 44 : if (aosTokensOffset.size() != nDimCount)
1208 : {
1209 1 : CPLError(CE_Failure, CPLE_AppDefined,
1210 : "Wrong number of values in offset");
1211 1 : return nullptr;
1212 : }
1213 128 : for (int i = 0; i < nDimCount; ++i)
1214 : {
1215 86 : anOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1216 86 : aosTokensOffset[i],
1217 86 : static_cast<int>(strlen(aosTokensOffset[i]))));
1218 171 : if (aosTokensOffset[i][0] == '-' ||
1219 85 : anOffset[i] >= dims[i]->GetSize())
1220 : {
1221 1 : CPLError(CE_Failure, CPLE_AppDefined,
1222 : "Wrong value in offset");
1223 1 : return nullptr;
1224 : }
1225 : }
1226 : }
1227 :
1228 620 : const char *pszCount = CPLGetXMLValue(psNode, "count", nullptr);
1229 620 : if (pszCount != nullptr)
1230 : {
1231 40 : CPLStringList aosTokensCount(CSLTokenizeString2(pszCount, ", ", 0));
1232 40 : if (aosTokensCount.size() != nDimCount)
1233 : {
1234 1 : CPLError(CE_Failure, CPLE_AppDefined,
1235 : "Wrong number of values in count");
1236 1 : return nullptr;
1237 : }
1238 115 : for (int i = 0; i < nDimCount; ++i)
1239 : {
1240 78 : anCount[i] = static_cast<size_t>(CPLScanUIntBig(
1241 78 : aosTokensCount[i],
1242 78 : static_cast<int>(strlen(aosTokensCount[i]))));
1243 154 : if (aosTokensCount[i][0] == '-' || anCount[i] == 0 ||
1244 76 : anOffset[i] + anCount[i] > dims[i]->GetSize())
1245 : {
1246 2 : CPLError(CE_Failure, CPLE_AppDefined,
1247 : "Wrong value in count");
1248 2 : return nullptr;
1249 : }
1250 : }
1251 : }
1252 : else
1253 : {
1254 1739 : for (int i = 0; i < nDimCount; ++i)
1255 : {
1256 1159 : anCount[i] =
1257 1159 : static_cast<size_t>(dims[i]->GetSize() - anOffset[i]);
1258 : }
1259 : }
1260 617 : if (!bIsConstantValue)
1261 : {
1262 504 : for (int i = 0; i < nDimCount; ++i)
1263 : {
1264 255 : if (anCount[i] >
1265 255 : std::numeric_limits<size_t>::max() / nArrayByteSize)
1266 : {
1267 0 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
1268 0 : return nullptr;
1269 : }
1270 255 : nArrayByteSize *= anCount[i];
1271 : }
1272 : }
1273 : }
1274 :
1275 620 : const size_t nExpectedVals = nArrayByteSize / nDTSize;
1276 1240 : CPLStringList aosValues;
1277 :
1278 620 : if (strcmp(psNode->pszValue, "InlineValuesWithValueElement") == 0)
1279 : {
1280 1244 : for (auto psIter = psNode->psChild; psIter; psIter = psIter->psNext)
1281 : {
1282 1000 : if (psIter->eType == CXT_Element &&
1283 972 : strcmp(psIter->pszValue, "Value") == 0)
1284 : {
1285 972 : aosValues.AddString(CPLGetXMLValue(psIter, nullptr, ""));
1286 : }
1287 : }
1288 : }
1289 : else
1290 : {
1291 376 : const char *pszValue = CPLGetXMLValue(psNode, nullptr, nullptr);
1292 376 : if (pszValue == nullptr ||
1293 376 : (!bIsConstantValue && nExpectedVals > strlen(pszValue)))
1294 : {
1295 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid content");
1296 1 : return nullptr;
1297 : }
1298 375 : aosValues.Assign(CSLTokenizeString2(pszValue, ", \r\n", 0), true);
1299 : }
1300 :
1301 619 : if (static_cast<size_t>(aosValues.size()) != nExpectedVals)
1302 : {
1303 0 : CPLError(CE_Failure, CPLE_AppDefined,
1304 : "Invalid number of values. Got %u, expected %u",
1305 0 : static_cast<unsigned>(aosValues.size()),
1306 : static_cast<unsigned>(nExpectedVals));
1307 0 : return nullptr;
1308 : }
1309 1238 : std::vector<GByte> abyValues;
1310 : try
1311 : {
1312 619 : abyValues.resize(nArrayByteSize);
1313 : }
1314 0 : catch (const std::exception &ex)
1315 : {
1316 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", ex.what());
1317 0 : return nullptr;
1318 : }
1319 :
1320 1238 : const auto dtString(GDALExtendedDataType::CreateString());
1321 619 : GByte *pabyPtr = &abyValues[0];
1322 2005 : for (int i = 0; i < aosValues.size(); ++i)
1323 : {
1324 1386 : const char *pszVal = &aosValues[i][0];
1325 1386 : GDALExtendedDataType::CopyValue(&pszVal, dtString, pabyPtr, dt);
1326 1386 : pabyPtr += nDTSize;
1327 : }
1328 :
1329 : return std::make_unique<VRTMDArraySourceInlinedValues>(
1330 619 : array, bIsConstantValue, std::move(anOffset), std::move(anCount),
1331 1238 : std::move(abyValues));
1332 : }
1333 :
1334 : /************************************************************************/
1335 : /* ~VRTMDArraySourceInlinedValues() */
1336 : /************************************************************************/
1337 :
1338 1238 : VRTMDArraySourceInlinedValues::~VRTMDArraySourceInlinedValues()
1339 : {
1340 619 : if (m_dt.NeedsFreeDynamicMemory())
1341 : {
1342 244 : const size_t nDTSize = m_dt.GetSize();
1343 244 : const size_t nValueCount = m_abyValues.size() / nDTSize;
1344 244 : GByte *pabyPtr = &m_abyValues[0];
1345 1216 : for (size_t i = 0; i < nValueCount; ++i)
1346 : {
1347 972 : m_dt.FreeDynamicMemory(pabyPtr);
1348 972 : pabyPtr += nDTSize;
1349 : }
1350 : }
1351 1238 : }
1352 :
1353 : /************************************************************************/
1354 : /* Read() */
1355 : /************************************************************************/
1356 2621 : static inline void IncrPointer(const GByte *&ptr, GInt64 nInc, size_t nIncSize)
1357 : {
1358 2621 : if (nInc < 0)
1359 7 : ptr -= (-nInc) * nIncSize;
1360 : else
1361 2614 : ptr += nInc * nIncSize;
1362 2621 : }
1363 :
1364 2621 : static inline void IncrPointer(GByte *&ptr, GPtrDiff_t nInc, size_t nIncSize)
1365 : {
1366 2621 : if (nInc < 0)
1367 0 : ptr -= (-nInc) * nIncSize;
1368 : else
1369 2621 : ptr += nInc * nIncSize;
1370 2621 : }
1371 :
1372 90 : bool VRTMDArraySourceInlinedValues::Read(
1373 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
1374 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
1375 : void *pDstBuffer) const
1376 : {
1377 90 : const auto nDims(m_poDstArray->GetDimensionCount());
1378 180 : std::vector<GUInt64> anReqStart(nDims);
1379 180 : std::vector<size_t> anReqCount(nDims);
1380 : // Compute the intersection between the inline value slab and the
1381 : // request slab.
1382 213 : for (size_t i = 0; i < nDims; i++)
1383 : {
1384 124 : auto start_i = arrayStartIdx[i];
1385 124 : auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1386 124 : if (arrayStep[i] < 0)
1387 : {
1388 : // For negative step request, temporarily simulate a positive step
1389 : // and fix up the start at the end of the loop.
1390 : // Use double negation so that operations occur only on
1391 : // positive quantities to avoid an artificial negative signed
1392 : // integer to unsigned conversion.
1393 9 : start_i = start_i - ((count[i] - 1) * (-step_i));
1394 9 : step_i = -step_i;
1395 : }
1396 :
1397 124 : const auto nRightDstOffsetFromConfig = m_anOffset[i] + m_anCount[i];
1398 248 : if (start_i >= nRightDstOffsetFromConfig ||
1399 124 : start_i + (count[i] - 1) * step_i < m_anOffset[i])
1400 : {
1401 1 : return true;
1402 : }
1403 123 : if (start_i < m_anOffset[i])
1404 : {
1405 11 : anReqStart[i] =
1406 11 : m_anOffset[i] +
1407 11 : (step_i - ((m_anOffset[i] - start_i) % step_i)) % step_i;
1408 : }
1409 : else
1410 : {
1411 112 : anReqStart[i] = start_i;
1412 : }
1413 246 : anReqCount[i] = 1 + static_cast<size_t>(
1414 369 : (std::min(nRightDstOffsetFromConfig - 1,
1415 123 : start_i + (count[i] - 1) * step_i) -
1416 123 : anReqStart[i]) /
1417 123 : step_i);
1418 123 : if (arrayStep[i] < 0)
1419 : {
1420 8 : anReqStart[i] = anReqStart[i] + (anReqCount[i] - 1) * step_i;
1421 : }
1422 : }
1423 :
1424 89 : size_t nSrcOffset = 0;
1425 89 : GPtrDiff_t nDstOffset = 0;
1426 89 : const auto nBufferDataTypeSize(bufferDataType.GetSize());
1427 211 : for (size_t i = 0; i < nDims; i++)
1428 : {
1429 : const size_t nRelStartSrc =
1430 122 : static_cast<size_t>(anReqStart[i] - m_anOffset[i]);
1431 122 : nSrcOffset += nRelStartSrc * m_anInlinedArrayStrideInBytes[i];
1432 : const size_t nRelStartDst =
1433 122 : static_cast<size_t>(anReqStart[i] - arrayStartIdx[i]);
1434 122 : nDstOffset += nRelStartDst * bufferStride[i] * nBufferDataTypeSize;
1435 : }
1436 178 : std::vector<const GByte *> abyStackSrcPtr(nDims + 1);
1437 89 : abyStackSrcPtr[0] = m_abyValues.data() + nSrcOffset;
1438 178 : std::vector<GByte *> abyStackDstPtr(nDims + 1);
1439 89 : abyStackDstPtr[0] = static_cast<GByte *>(pDstBuffer) + nDstOffset;
1440 :
1441 89 : const auto &dt(m_poDstArray->GetDataType());
1442 89 : std::vector<size_t> anStackCount(nDims);
1443 89 : size_t iDim = 0;
1444 :
1445 3086 : lbl_next_depth:
1446 3086 : if (iDim == nDims)
1447 : {
1448 2710 : GDALExtendedDataType::CopyValue(abyStackSrcPtr[nDims], dt,
1449 2710 : abyStackDstPtr[nDims], bufferDataType);
1450 : }
1451 : else
1452 : {
1453 376 : anStackCount[iDim] = anReqCount[iDim];
1454 : while (true)
1455 : {
1456 2997 : ++iDim;
1457 2997 : abyStackSrcPtr[iDim] = abyStackSrcPtr[iDim - 1];
1458 2997 : abyStackDstPtr[iDim] = abyStackDstPtr[iDim - 1];
1459 2997 : goto lbl_next_depth;
1460 2997 : lbl_return_to_caller:
1461 2997 : --iDim;
1462 2997 : --anStackCount[iDim];
1463 2997 : if (anStackCount[iDim] == 0)
1464 376 : break;
1465 2621 : IncrPointer(abyStackSrcPtr[iDim], arrayStep[iDim],
1466 2621 : m_anInlinedArrayStrideInBytes[iDim]);
1467 2621 : IncrPointer(abyStackDstPtr[iDim], bufferStride[iDim],
1468 : nBufferDataTypeSize);
1469 : }
1470 : }
1471 3086 : if (iDim > 0)
1472 2997 : goto lbl_return_to_caller;
1473 :
1474 89 : return true;
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* Serialize() */
1479 : /************************************************************************/
1480 :
1481 29 : void VRTMDArraySourceInlinedValues::Serialize(CPLXMLNode *psParent,
1482 : const char *) const
1483 : {
1484 29 : const auto &dt(m_poDstArray->GetDataType());
1485 29 : CPLXMLNode *psSource = CPLCreateXMLNode(psParent, CXT_Element,
1486 29 : m_bIsConstantValue ? "ConstantValue"
1487 13 : : dt.GetClass() == GEDTC_STRING
1488 13 : ? "InlineValuesWithValueElement"
1489 : : "InlineValues");
1490 :
1491 58 : std::string osOffset;
1492 84 : for (auto nOffset : m_anOffset)
1493 : {
1494 55 : if (!osOffset.empty())
1495 27 : osOffset += ',';
1496 55 : osOffset += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nOffset));
1497 : }
1498 29 : if (!osOffset.empty())
1499 : {
1500 28 : CPLAddXMLAttributeAndValue(psSource, "offset", osOffset.c_str());
1501 : }
1502 :
1503 58 : std::string osCount;
1504 29 : size_t nValues = 1;
1505 84 : for (auto nCount : m_anCount)
1506 : {
1507 55 : if (!osCount.empty())
1508 27 : osCount += ',';
1509 55 : nValues *= nCount;
1510 55 : osCount += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nCount));
1511 : }
1512 29 : if (!osCount.empty())
1513 : {
1514 28 : CPLAddXMLAttributeAndValue(psSource, "count", osCount.c_str());
1515 : }
1516 :
1517 58 : const auto dtString(GDALExtendedDataType::CreateString());
1518 29 : const size_t nDTSize(dt.GetSize());
1519 29 : if (dt.GetClass() == GEDTC_STRING)
1520 : {
1521 12 : CPLXMLNode *psLast = psSource->psChild;
1522 12 : if (psLast)
1523 : {
1524 22 : while (psLast->psNext)
1525 11 : psLast = psLast->psNext;
1526 : }
1527 56 : for (size_t i = 0; i < (m_bIsConstantValue ? 1 : nValues); ++i)
1528 : {
1529 44 : char *pszStr = nullptr;
1530 44 : GDALExtendedDataType::CopyValue(&m_abyValues[i * nDTSize], dt,
1531 : &pszStr, dtString);
1532 44 : if (pszStr)
1533 : {
1534 : auto psNode =
1535 44 : CPLCreateXMLElementAndValue(nullptr, "Value", pszStr);
1536 44 : if (psLast)
1537 43 : psLast->psNext = psNode;
1538 : else
1539 1 : psSource->psChild = psNode;
1540 44 : psLast = psNode;
1541 44 : CPLFree(pszStr);
1542 : }
1543 : }
1544 : }
1545 : else
1546 : {
1547 34 : std::string osValues;
1548 45 : for (size_t i = 0; i < (m_bIsConstantValue ? 1 : nValues); ++i)
1549 : {
1550 28 : if (i > 0)
1551 11 : osValues += ' ';
1552 28 : char *pszStr = nullptr;
1553 28 : GDALExtendedDataType::CopyValue(&m_abyValues[i * nDTSize], dt,
1554 : &pszStr, dtString);
1555 28 : if (pszStr)
1556 : {
1557 28 : osValues += pszStr;
1558 28 : CPLFree(pszStr);
1559 : }
1560 : }
1561 17 : CPLCreateXMLNode(psSource, CXT_Text, osValues.c_str());
1562 : }
1563 29 : }
1564 :
1565 : /************************************************************************/
1566 : /* Create() */
1567 : /************************************************************************/
1568 :
1569 : std::unique_ptr<VRTMDArraySourceFromArray>
1570 133 : VRTMDArraySourceFromArray::Create(const VRTMDArray *poDstArray,
1571 : const CPLXMLNode *psNode)
1572 : {
1573 133 : const char *pszFilename = CPLGetXMLValue(psNode, "SourceFilename", nullptr);
1574 133 : if (pszFilename == nullptr)
1575 : {
1576 0 : CPLError(CE_Failure, CPLE_AppDefined, "SourceFilename element missing");
1577 0 : return nullptr;
1578 : }
1579 : const char *pszRelativeToVRT =
1580 133 : CPLGetXMLValue(psNode, "SourceFilename.relativetoVRT", nullptr);
1581 133 : const bool bRelativeToVRTSet = pszRelativeToVRT != nullptr;
1582 : const bool bRelativeToVRT =
1583 133 : pszRelativeToVRT ? CPL_TO_BOOL(atoi(pszRelativeToVRT)) : false;
1584 133 : const char *pszArray = CPLGetXMLValue(psNode, "SourceArray", "");
1585 133 : const char *pszSourceBand = CPLGetXMLValue(psNode, "SourceBand", "");
1586 133 : if (pszArray[0] == '\0' && pszSourceBand[0] == '\0')
1587 : {
1588 0 : CPLError(CE_Failure, CPLE_AppDefined,
1589 : "SourceArray or SourceBand element missing or empty");
1590 0 : return nullptr;
1591 : }
1592 133 : if (pszArray[0] != '\0' && pszSourceBand[0] != '\0')
1593 : {
1594 0 : CPLError(CE_Failure, CPLE_AppDefined,
1595 : "SourceArray and SourceBand are exclusive");
1596 0 : return nullptr;
1597 : }
1598 :
1599 133 : const char *pszTranspose = CPLGetXMLValue(psNode, "SourceTranspose", "");
1600 266 : std::vector<int> anTransposedAxis;
1601 266 : CPLStringList aosTransposedAxis(CSLTokenizeString2(pszTranspose, ",", 0));
1602 141 : for (int i = 0; i < aosTransposedAxis.size(); i++)
1603 8 : anTransposedAxis.push_back(atoi(aosTransposedAxis[i]));
1604 :
1605 133 : const char *pszView = CPLGetXMLValue(psNode, "SourceView", "");
1606 :
1607 133 : const int nDimCount = static_cast<int>(poDstArray->GetDimensionCount());
1608 266 : std::vector<GUInt64> anSrcOffset(nDimCount);
1609 266 : std::vector<GUInt64> anCount(nDimCount);
1610 266 : std::vector<GUInt64> anStep(nDimCount, 1);
1611 266 : std::vector<GUInt64> anDstOffset(nDimCount);
1612 :
1613 133 : if (nDimCount > 0)
1614 : {
1615 115 : const CPLXMLNode *psSourceSlab = CPLGetXMLNode(psNode, "SourceSlab");
1616 115 : if (psSourceSlab)
1617 : {
1618 : const char *pszOffset =
1619 102 : CPLGetXMLValue(psSourceSlab, "offset", nullptr);
1620 102 : if (pszOffset != nullptr)
1621 : {
1622 : CPLStringList aosTokensOffset(
1623 102 : CSLTokenizeString2(pszOffset, ", ", 0));
1624 102 : if (aosTokensOffset.size() != nDimCount)
1625 : {
1626 0 : CPLError(CE_Failure, CPLE_AppDefined,
1627 : "Wrong number of values in offset");
1628 0 : return nullptr;
1629 : }
1630 239 : for (int i = 0; i < nDimCount; ++i)
1631 : {
1632 137 : anSrcOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1633 137 : aosTokensOffset[i],
1634 137 : static_cast<int>(strlen(aosTokensOffset[i]))));
1635 137 : if (aosTokensOffset[i][0] == '-')
1636 : {
1637 0 : CPLError(CE_Failure, CPLE_AppDefined,
1638 : "Wrong value in offset");
1639 0 : return nullptr;
1640 : }
1641 : }
1642 : }
1643 :
1644 102 : const char *pszStep = CPLGetXMLValue(psSourceSlab, "step", nullptr);
1645 102 : if (pszStep != nullptr)
1646 : {
1647 : CPLStringList aosTokensStep(
1648 102 : CSLTokenizeString2(pszStep, ", ", 0));
1649 102 : if (aosTokensStep.size() != nDimCount)
1650 : {
1651 0 : CPLError(CE_Failure, CPLE_AppDefined,
1652 : "Wrong number of values in step");
1653 0 : return nullptr;
1654 : }
1655 239 : for (int i = 0; i < nDimCount; ++i)
1656 : {
1657 137 : anStep[i] = static_cast<GUInt64>(CPLScanUIntBig(
1658 137 : aosTokensStep[i],
1659 137 : static_cast<int>(strlen(aosTokensStep[i]))));
1660 137 : if (aosTokensStep[i][0] == '-')
1661 : {
1662 0 : CPLError(CE_Failure, CPLE_AppDefined,
1663 : "Wrong value in step");
1664 0 : return nullptr;
1665 : }
1666 : }
1667 : }
1668 :
1669 : const char *pszCount =
1670 102 : CPLGetXMLValue(psSourceSlab, "count", nullptr);
1671 102 : if (pszCount != nullptr)
1672 : {
1673 : CPLStringList aosTokensCount(
1674 102 : CSLTokenizeString2(pszCount, ", ", 0));
1675 102 : if (aosTokensCount.size() != nDimCount)
1676 : {
1677 0 : CPLError(CE_Failure, CPLE_AppDefined,
1678 : "Wrong number of values in count");
1679 0 : return nullptr;
1680 : }
1681 239 : for (int i = 0; i < nDimCount; ++i)
1682 : {
1683 137 : anCount[i] = static_cast<GUInt64>(CPLScanUIntBig(
1684 137 : aosTokensCount[i],
1685 137 : static_cast<int>(strlen(aosTokensCount[i]))));
1686 137 : if (aosTokensCount[i][0] == '-')
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_AppDefined,
1689 : "Wrong value in count");
1690 0 : return nullptr;
1691 : }
1692 : }
1693 : }
1694 : }
1695 :
1696 115 : const CPLXMLNode *psDestSlab = CPLGetXMLNode(psNode, "DestSlab");
1697 115 : if (psDestSlab)
1698 : {
1699 102 : const auto &dims(poDstArray->GetDimensions());
1700 : const char *pszOffset =
1701 102 : CPLGetXMLValue(psDestSlab, "offset", nullptr);
1702 102 : if (pszOffset != nullptr)
1703 : {
1704 : CPLStringList aosTokensOffset(
1705 102 : CSLTokenizeString2(pszOffset, ", ", 0));
1706 102 : if (aosTokensOffset.size() != nDimCount)
1707 : {
1708 0 : CPLError(CE_Failure, CPLE_AppDefined,
1709 : "Wrong number of values in offset");
1710 0 : return nullptr;
1711 : }
1712 239 : for (int i = 0; i < nDimCount; ++i)
1713 : {
1714 137 : anDstOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1715 137 : aosTokensOffset[i],
1716 137 : static_cast<int>(strlen(aosTokensOffset[i]))));
1717 274 : if (aosTokensOffset[i][0] == '-' ||
1718 137 : anDstOffset[i] >= dims[i]->GetSize())
1719 : {
1720 0 : CPLError(CE_Failure, CPLE_AppDefined,
1721 : "Wrong value in offset");
1722 0 : return nullptr;
1723 : }
1724 : }
1725 : }
1726 : }
1727 : }
1728 :
1729 : return std::make_unique<VRTMDArraySourceFromArray>(
1730 : poDstArray, bRelativeToVRTSet, bRelativeToVRT, pszFilename, pszArray,
1731 133 : pszSourceBand, std::move(anTransposedAxis), pszView,
1732 133 : std::move(anSrcOffset), std::move(anCount), std::move(anStep),
1733 266 : std::move(anDstOffset));
1734 : }
1735 :
1736 : /************************************************************************/
1737 : /* Serialize() */
1738 : /************************************************************************/
1739 :
1740 110 : void VRTMDArraySourceFromArray::Serialize(CPLXMLNode *psParent,
1741 : const char *pszVRTPath) const
1742 : {
1743 110 : CPLXMLNode *psSource = CPLCreateXMLNode(psParent, CXT_Element, "Source");
1744 :
1745 110 : if (m_bRelativeToVRTSet)
1746 : {
1747 0 : auto psSourceFilename = CPLCreateXMLElementAndValue(
1748 : psSource, "SourceFilename", m_osFilename.c_str());
1749 0 : if (m_bRelativeToVRT)
1750 : {
1751 0 : CPLAddXMLAttributeAndValue(psSourceFilename, "relativetoVRT", "1");
1752 : }
1753 : }
1754 : else
1755 : {
1756 110 : int bRelativeToVRT = FALSE;
1757 110 : const char *pszSourceFilename = CPLExtractRelativePath(
1758 : pszVRTPath, m_osFilename.c_str(), &bRelativeToVRT);
1759 110 : auto psSourceFilename = CPLCreateXMLElementAndValue(
1760 : psSource, "SourceFilename", pszSourceFilename);
1761 110 : if (bRelativeToVRT)
1762 : {
1763 3 : CPLAddXMLAttributeAndValue(psSourceFilename, "relativetoVRT", "1");
1764 : }
1765 : }
1766 :
1767 110 : if (!m_osArray.empty())
1768 108 : CPLCreateXMLElementAndValue(psSource, "SourceArray", m_osArray.c_str());
1769 : else
1770 2 : CPLCreateXMLElementAndValue(psSource, "SourceBand", m_osBand.c_str());
1771 :
1772 110 : if (!m_anTransposedAxis.empty())
1773 : {
1774 4 : std::string str;
1775 7 : for (size_t i = 0; i < m_anTransposedAxis.size(); i++)
1776 : {
1777 5 : if (i > 0)
1778 3 : str += ',';
1779 5 : str += CPLSPrintf("%d", m_anTransposedAxis[i]);
1780 : }
1781 2 : CPLCreateXMLElementAndValue(psSource, "SourceTranspose", str.c_str());
1782 : }
1783 :
1784 110 : if (!m_osViewExpr.empty())
1785 : {
1786 70 : CPLCreateXMLElementAndValue(psSource, "SourceView",
1787 : m_osViewExpr.c_str());
1788 : }
1789 :
1790 110 : if (m_poDstArray->GetDimensionCount() > 0)
1791 : {
1792 : CPLXMLNode *psSourceSlab =
1793 92 : CPLCreateXMLNode(psSource, CXT_Element, "SourceSlab");
1794 : {
1795 184 : std::string str;
1796 211 : for (size_t i = 0; i < m_anSrcOffset.size(); i++)
1797 : {
1798 119 : if (i > 0)
1799 27 : str += ',';
1800 : str += CPLSPrintf(CPL_FRMT_GUIB,
1801 119 : static_cast<GUIntBig>(m_anSrcOffset[i]));
1802 : }
1803 92 : CPLAddXMLAttributeAndValue(psSourceSlab, "offset", str.c_str());
1804 : }
1805 : {
1806 184 : std::string str;
1807 211 : for (size_t i = 0; i < m_anCount.size(); i++)
1808 : {
1809 119 : if (i > 0)
1810 27 : str += ',';
1811 : str += CPLSPrintf(CPL_FRMT_GUIB,
1812 119 : static_cast<GUIntBig>(m_anCount[i]));
1813 : }
1814 92 : CPLAddXMLAttributeAndValue(psSourceSlab, "count", str.c_str());
1815 : }
1816 : {
1817 184 : std::string str;
1818 211 : for (size_t i = 0; i < m_anStep.size(); i++)
1819 : {
1820 119 : if (i > 0)
1821 27 : str += ',';
1822 : str += CPLSPrintf(CPL_FRMT_GUIB,
1823 119 : static_cast<GUIntBig>(m_anStep[i]));
1824 : }
1825 92 : CPLAddXMLAttributeAndValue(psSourceSlab, "step", str.c_str());
1826 : }
1827 :
1828 : CPLXMLNode *psDestSlab =
1829 92 : CPLCreateXMLNode(psSource, CXT_Element, "DestSlab");
1830 : {
1831 184 : std::string str;
1832 211 : for (size_t i = 0; i < m_anDstOffset.size(); i++)
1833 : {
1834 119 : if (i > 0)
1835 27 : str += ',';
1836 : str += CPLSPrintf(CPL_FRMT_GUIB,
1837 119 : static_cast<GUIntBig>(m_anDstOffset[i]));
1838 : }
1839 92 : CPLAddXMLAttributeAndValue(psDestSlab, "offset", str.c_str());
1840 : }
1841 : }
1842 110 : }
1843 :
1844 : /************************************************************************/
1845 : /* ~VRTMDArraySourceFromArray() */
1846 : /************************************************************************/
1847 :
1848 512 : VRTMDArraySourceFromArray::~VRTMDArraySourceFromArray()
1849 : {
1850 512 : std::lock_guard<std::mutex> oGuard(g_cacheLock);
1851 :
1852 : // Remove from the cache datasets that are only used by this array
1853 : // or drop our reference to those datasets
1854 512 : std::unordered_set<std::string> oSetKeysToRemove;
1855 512 : std::unordered_set<std::string> oSetKeysToDropReference;
1856 30 : auto lambda = [&oSetKeysToRemove, &oSetKeysToDropReference,
1857 58 : this](const decltype(g_cacheSources)::node_type &key_value)
1858 : {
1859 30 : auto &listOfArrays(key_value.value.second);
1860 30 : auto oIter = listOfArrays.find(this);
1861 30 : if (oIter != listOfArrays.end())
1862 : {
1863 28 : if (listOfArrays.size() == 1)
1864 9 : oSetKeysToRemove.insert(key_value.key);
1865 : else
1866 19 : oSetKeysToDropReference.insert(key_value.key);
1867 : }
1868 286 : };
1869 256 : g_cacheSources.cwalk(lambda);
1870 265 : for (const auto &key : oSetKeysToRemove)
1871 : {
1872 9 : CPLDebug("VRT", "Dropping %s", key.c_str());
1873 9 : g_cacheSources.remove(key);
1874 : }
1875 275 : for (const auto &key : oSetKeysToDropReference)
1876 : {
1877 19 : CPLDebug("VRT", "Dropping reference to %s", key.c_str());
1878 38 : CacheEntry oPair;
1879 19 : g_cacheSources.tryGet(key, oPair);
1880 19 : oPair.second.erase(this);
1881 19 : g_cacheSources.insert(key, oPair);
1882 : }
1883 512 : }
1884 :
1885 : /************************************************************************/
1886 : /* Read() */
1887 : /************************************************************************/
1888 :
1889 37 : static std::string CreateKey(const std::string &filename)
1890 : {
1891 37 : return filename + CPLSPrintf("__thread_" CPL_FRMT_GIB, CPLGetPID());
1892 : }
1893 :
1894 38 : bool VRTMDArraySourceFromArray::Read(const GUInt64 *arrayStartIdx,
1895 : const size_t *count,
1896 : const GInt64 *arrayStep,
1897 : const GPtrDiff_t *bufferStride,
1898 : const GDALExtendedDataType &bufferDataType,
1899 : void *pDstBuffer) const
1900 : {
1901 : // Preliminary check without trying to open source array
1902 38 : const auto nDims(m_poDstArray->GetDimensionCount());
1903 101 : for (size_t i = 0; i < nDims; i++)
1904 : {
1905 64 : auto start_i = arrayStartIdx[i];
1906 64 : auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1907 64 : if (arrayStep[i] < 0)
1908 : {
1909 : // For negative step request, temporarily simulate a positive step
1910 2 : start_i = start_i - (m_anCount[i] - 1) * (-step_i);
1911 2 : step_i = -step_i;
1912 : }
1913 64 : if (start_i + (count[i] - 1) * step_i < m_anDstOffset[i])
1914 : {
1915 1 : return true;
1916 : }
1917 : }
1918 :
1919 76 : for (size_t i = 0; i < nDims; i++)
1920 : {
1921 51 : if (m_anCount[i] == 0) // we need to open the array...
1922 12 : break;
1923 :
1924 39 : auto start_i = arrayStartIdx[i];
1925 39 : auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1926 39 : if (arrayStep[i] < 0)
1927 : {
1928 : // For negative step request, temporarily simulate a positive step
1929 2 : start_i = start_i - (m_anCount[i] - 1) * (-step_i);
1930 : // step_i = -step_i;
1931 : }
1932 39 : if (start_i >= m_anDstOffset[i] + m_anCount[i])
1933 : {
1934 0 : return true;
1935 : }
1936 : }
1937 :
1938 : const std::string osFilename =
1939 37 : m_bRelativeToVRT
1940 1 : ? CPLProjectRelativeFilenameSafe(m_poDstArray->GetVRTPath().c_str(),
1941 : m_osFilename.c_str())
1942 75 : : m_osFilename;
1943 74 : const std::string key(CreateKey(osFilename));
1944 :
1945 37 : std::shared_ptr<VRTArrayDatasetWrapper> poSrcDSWrapper;
1946 : GDALDataset *poSrcDS;
1947 74 : CacheEntry oPair;
1948 : {
1949 37 : std::lock_guard<std::mutex> oGuard(g_cacheLock);
1950 37 : if (g_cacheSources.tryGet(key, oPair))
1951 : {
1952 26 : poSrcDSWrapper = oPair.first;
1953 26 : poSrcDS = poSrcDSWrapper.get()->get();
1954 26 : if (oPair.second.find(this) == oPair.second.end())
1955 : {
1956 19 : oPair.second.insert(this);
1957 19 : g_cacheSources.insert(key, oPair);
1958 : }
1959 : }
1960 : else
1961 : {
1962 11 : poSrcDS = GDALDataset::Open(
1963 : osFilename.c_str(),
1964 11 : (m_osBand.empty() ? GDAL_OF_MULTIDIM_RASTER : GDAL_OF_RASTER) |
1965 : GDAL_OF_INTERNAL | GDAL_OF_VERBOSE_ERROR,
1966 : nullptr, nullptr, nullptr);
1967 11 : if (!poSrcDS)
1968 2 : return false;
1969 9 : poSrcDSWrapper = std::make_shared<VRTArrayDatasetWrapper>(poSrcDS);
1970 9 : oPair.first = std::move(poSrcDSWrapper);
1971 9 : oPair.second.insert(this);
1972 9 : g_cacheSources.insert(key, oPair);
1973 : }
1974 : }
1975 :
1976 35 : std::shared_ptr<GDALMDArray> poArray;
1977 35 : if (m_osBand.empty())
1978 : {
1979 33 : auto rg(poSrcDS->GetRootGroup());
1980 33 : if (rg == nullptr)
1981 0 : return false;
1982 :
1983 33 : auto curGroup(rg);
1984 33 : std::string arrayName(m_osArray);
1985 66 : poArray = m_osArray[0] == '/' ? rg->OpenMDArrayFromFullname(arrayName)
1986 33 : : curGroup->OpenMDArray(arrayName);
1987 33 : if (poArray == nullptr)
1988 : {
1989 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s",
1990 : m_osArray.c_str());
1991 1 : return false;
1992 : }
1993 : }
1994 : else
1995 : {
1996 2 : int nSrcBand = atoi(m_osBand.c_str());
1997 2 : auto poBand = poSrcDS->GetRasterBand(nSrcBand);
1998 2 : if (poBand == nullptr)
1999 1 : return false;
2000 1 : poArray = poBand->AsMDArray();
2001 1 : CPLAssert(poArray);
2002 : }
2003 :
2004 66 : std::string osViewExpr = m_osViewExpr;
2005 66 : if (STARTS_WITH(osViewExpr.c_str(), "resample=true,") ||
2006 33 : osViewExpr == "resample=true")
2007 : {
2008 : poArray =
2009 3 : poArray->GetResampled(std::vector<std::shared_ptr<GDALDimension>>(
2010 1 : poArray->GetDimensionCount()),
2011 2 : GRIORA_NearestNeighbour, nullptr, nullptr);
2012 1 : if (poArray == nullptr)
2013 : {
2014 0 : return false;
2015 : }
2016 1 : if (osViewExpr == "resample=true")
2017 1 : osViewExpr.clear();
2018 : else
2019 0 : osViewExpr = osViewExpr.substr(strlen("resample=true,"));
2020 : }
2021 :
2022 33 : if (!m_anTransposedAxis.empty())
2023 : {
2024 2 : poArray = poArray->Transpose(m_anTransposedAxis);
2025 2 : if (poArray == nullptr)
2026 : {
2027 1 : return false;
2028 : }
2029 : }
2030 32 : if (!osViewExpr.empty())
2031 : {
2032 4 : poArray = poArray->GetView(osViewExpr);
2033 4 : if (poArray == nullptr)
2034 : {
2035 1 : return false;
2036 : }
2037 : }
2038 31 : if (m_poDstArray->GetDimensionCount() != poArray->GetDimensionCount())
2039 : {
2040 1 : CPLError(CE_Failure, CPLE_AppDefined,
2041 : "Inconsistent number of dimensions");
2042 1 : return false;
2043 : }
2044 :
2045 30 : const auto &srcDims(poArray->GetDimensions());
2046 60 : std::vector<GUInt64> anReqDstStart(nDims);
2047 60 : std::vector<size_t> anReqCount(nDims);
2048 : // Compute the intersection between the inline value slab and the
2049 : // request slab.
2050 77 : for (size_t i = 0; i < nDims; i++)
2051 : {
2052 48 : if (m_anSrcOffset[i] >= srcDims[i]->GetSize())
2053 : {
2054 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid SourceSlab.offset");
2055 1 : return false;
2056 : }
2057 47 : auto start_i = arrayStartIdx[i];
2058 47 : auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
2059 47 : if (arrayStep[i] < 0)
2060 : {
2061 2 : if (m_anCount[i] == 0)
2062 0 : m_anCount[i] = (m_anSrcOffset[i] + 1) / -step_i;
2063 : // For negative step request, temporarily simulate a positive step
2064 : // and fix up the start at the end of the loop.
2065 2 : start_i = start_i - (m_anCount[i] - 1) * (-step_i);
2066 2 : step_i = -step_i;
2067 : }
2068 : else
2069 : {
2070 45 : if (m_anCount[i] == 0)
2071 10 : m_anCount[i] =
2072 10 : (srcDims[i]->GetSize() - m_anSrcOffset[i]) / step_i;
2073 : }
2074 :
2075 47 : const auto nRightDstOffsetFromConfig = m_anDstOffset[i] + m_anCount[i];
2076 47 : if (start_i >= nRightDstOffsetFromConfig)
2077 : {
2078 0 : return true;
2079 : }
2080 47 : if (start_i < m_anDstOffset[i])
2081 : {
2082 7 : anReqDstStart[i] =
2083 7 : m_anDstOffset[i] +
2084 7 : (step_i - ((m_anDstOffset[i] - start_i) % step_i)) % step_i;
2085 : }
2086 : else
2087 : {
2088 40 : anReqDstStart[i] = start_i;
2089 : }
2090 94 : anReqCount[i] = 1 + static_cast<size_t>(
2091 141 : (std::min(nRightDstOffsetFromConfig - 1,
2092 47 : start_i + (count[i] - 1) * step_i) -
2093 47 : anReqDstStart[i]) /
2094 47 : step_i);
2095 47 : if (arrayStep[i] < 0)
2096 : {
2097 2 : anReqDstStart[i] = anReqDstStart[i] + (anReqCount[i] - 1) * step_i;
2098 : }
2099 : }
2100 :
2101 29 : GPtrDiff_t nDstOffset = 0;
2102 29 : const auto nBufferDataTypeSize(bufferDataType.GetSize());
2103 58 : std::vector<GUInt64> anSrcArrayOffset(nDims);
2104 58 : std::vector<GInt64> anSrcArrayStep(nDims);
2105 76 : for (size_t i = 0; i < nDims; i++)
2106 : {
2107 : const size_t nRelStartDst =
2108 47 : static_cast<size_t>(anReqDstStart[i] - arrayStartIdx[i]);
2109 47 : nDstOffset += nRelStartDst * bufferStride[i] * nBufferDataTypeSize;
2110 94 : anSrcArrayOffset[i] =
2111 47 : m_anSrcOffset[i] +
2112 47 : (anReqDstStart[i] - m_anDstOffset[i]) * m_anStep[i];
2113 47 : if (arrayStep[i] < 0)
2114 2 : anSrcArrayStep[i] = -static_cast<GInt64>(
2115 2 : m_anStep[i] * static_cast<GUInt64>(-arrayStep[i]));
2116 : else
2117 45 : anSrcArrayStep[i] = m_anStep[i] * arrayStep[i];
2118 : }
2119 58 : return poArray->Read(anSrcArrayOffset.data(), anReqCount.data(),
2120 29 : anSrcArrayStep.data(), bufferStride, bufferDataType,
2121 58 : static_cast<GByte *>(pDstBuffer) + nDstOffset);
2122 : }
2123 :
2124 : /************************************************************************/
2125 : /* IRead() */
2126 : /************************************************************************/
2127 :
2128 189 : bool VRTMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
2129 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
2130 : const GDALExtendedDataType &bufferDataType,
2131 : void *pDstBuffer) const
2132 : {
2133 189 : const auto nDims(m_dims.size());
2134 :
2135 : // Initialize pDstBuffer
2136 189 : bool bFullyCompactStride = true;
2137 378 : std::map<size_t, size_t> mapStrideToIdx;
2138 426 : for (size_t i = 0; i < nDims; i++)
2139 : {
2140 474 : if (bufferStride[i] < 0 ||
2141 237 : mapStrideToIdx.find(static_cast<size_t>(bufferStride[i])) !=
2142 474 : mapStrideToIdx.end())
2143 : {
2144 0 : bFullyCompactStride = false;
2145 0 : break;
2146 : }
2147 237 : mapStrideToIdx[static_cast<size_t>(bufferStride[i])] = i;
2148 : }
2149 189 : size_t nAccStride = 1;
2150 189 : if (bFullyCompactStride)
2151 : {
2152 425 : for (size_t i = 0; i < nDims; i++)
2153 : {
2154 237 : auto oIter = mapStrideToIdx.find(nAccStride);
2155 237 : if (oIter == mapStrideToIdx.end())
2156 : {
2157 1 : bFullyCompactStride = false;
2158 1 : break;
2159 : }
2160 236 : nAccStride = nAccStride * count[oIter->second];
2161 : }
2162 : }
2163 :
2164 189 : const auto nDTSize(m_dt.GetSize());
2165 189 : const auto nBufferDTSize(bufferDataType.GetSize());
2166 189 : const GByte *pabyNoData = static_cast<const GByte *>(GetRawNoDataValue());
2167 378 : std::vector<GByte> abyFill;
2168 189 : if (pabyNoData)
2169 : {
2170 5 : bool bAllZero = true;
2171 13 : for (size_t i = 0; i < nDTSize; i++)
2172 : {
2173 12 : if (pabyNoData[i])
2174 : {
2175 4 : bAllZero = false;
2176 4 : break;
2177 : }
2178 : }
2179 5 : if (bAllZero)
2180 : {
2181 1 : pabyNoData = nullptr;
2182 : }
2183 : else
2184 : {
2185 4 : abyFill.resize(nBufferDTSize);
2186 4 : GDALExtendedDataType::CopyValue(pabyNoData, m_dt, &abyFill[0],
2187 : bufferDataType);
2188 : }
2189 : }
2190 :
2191 189 : if (bFullyCompactStride)
2192 : {
2193 188 : if (pabyNoData == nullptr)
2194 : {
2195 184 : memset(pDstBuffer, 0, nAccStride * nBufferDTSize);
2196 : }
2197 4 : else if (bufferDataType.NeedsFreeDynamicMemory())
2198 : {
2199 0 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2200 0 : for (size_t i = 0; i < nAccStride; i++)
2201 : {
2202 0 : GDALExtendedDataType::CopyValue(pabyDstBuffer, bufferDataType,
2203 0 : &abyFill[0], bufferDataType);
2204 0 : pabyDstBuffer += nBufferDTSize;
2205 : }
2206 : }
2207 : else
2208 : {
2209 4 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2210 824 : for (size_t i = 0; i < nAccStride; i++)
2211 : {
2212 820 : memcpy(pabyDstBuffer, &abyFill[0], nBufferDTSize);
2213 820 : pabyDstBuffer += nBufferDTSize;
2214 : }
2215 : }
2216 : }
2217 : else
2218 : {
2219 : const bool bNeedsDynamicMemory =
2220 1 : bufferDataType.NeedsFreeDynamicMemory();
2221 2 : std::vector<size_t> anStackCount(nDims);
2222 2 : std::vector<GByte *> abyStackDstPtr;
2223 1 : size_t iDim = 0;
2224 1 : abyStackDstPtr.push_back(static_cast<GByte *>(pDstBuffer));
2225 1 : abyStackDstPtr.resize(nDims + 1);
2226 7 : lbl_next_depth:
2227 7 : if (iDim == nDims)
2228 : {
2229 4 : if (pabyNoData == nullptr)
2230 : {
2231 4 : memset(abyStackDstPtr[nDims], 0, nBufferDTSize);
2232 : }
2233 0 : else if (bNeedsDynamicMemory)
2234 : {
2235 0 : GDALExtendedDataType::CopyValue(abyStackDstPtr[nDims],
2236 0 : bufferDataType, &abyFill[0],
2237 : bufferDataType);
2238 : }
2239 : else
2240 : {
2241 0 : memcpy(abyStackDstPtr[nDims], &abyFill[0], nBufferDTSize);
2242 : }
2243 : }
2244 : else
2245 : {
2246 3 : anStackCount[iDim] = count[iDim];
2247 : while (true)
2248 : {
2249 6 : ++iDim;
2250 6 : abyStackDstPtr[iDim] = abyStackDstPtr[iDim - 1];
2251 6 : goto lbl_next_depth;
2252 6 : lbl_return_to_caller:
2253 6 : --iDim;
2254 6 : --anStackCount[iDim];
2255 6 : if (anStackCount[iDim] == 0)
2256 3 : break;
2257 3 : abyStackDstPtr[iDim] += bufferStride[iDim] * nBufferDTSize;
2258 : }
2259 : }
2260 7 : if (iDim > 0)
2261 6 : goto lbl_return_to_caller;
2262 : }
2263 :
2264 189 : if (!abyFill.empty())
2265 : {
2266 4 : bufferDataType.FreeDynamicMemory(&abyFill[0]);
2267 : }
2268 :
2269 375 : for (const auto &poSource : m_sources)
2270 : {
2271 388 : if (!poSource->Read(arrayStartIdx, count, arrayStep, bufferStride,
2272 194 : bufferDataType, pDstBuffer))
2273 : {
2274 8 : return false;
2275 : }
2276 : }
2277 181 : return true;
2278 : }
2279 :
2280 : /************************************************************************/
2281 : /* SetDirty() */
2282 : /************************************************************************/
2283 :
2284 2215 : void VRTMDArray::SetDirty()
2285 : {
2286 2215 : auto poGroup(GetGroup());
2287 2215 : if (poGroup)
2288 : {
2289 2215 : poGroup->SetDirty();
2290 : }
2291 2215 : }
2292 :
2293 : /************************************************************************/
2294 : /* GetGroup() */
2295 : /************************************************************************/
2296 :
2297 2439 : VRTGroup *VRTMDArray::GetGroup() const
2298 : {
2299 2439 : auto ref = m_poGroupRef.lock();
2300 2439 : return ref ? ref->m_ptr : nullptr;
2301 : }
2302 :
2303 : /************************************************************************/
2304 : /* CreateAttribute() */
2305 : /************************************************************************/
2306 :
2307 : std::shared_ptr<GDALAttribute>
2308 59 : VRTMDArray::CreateAttribute(const std::string &osName,
2309 : const std::vector<GUInt64> &anDimensions,
2310 : const GDALExtendedDataType &oDataType, CSLConstList)
2311 : {
2312 59 : if (!VRTAttribute::CreationCommonChecks(osName, anDimensions,
2313 59 : m_oMapAttributes))
2314 : {
2315 2 : return nullptr;
2316 : }
2317 57 : SetDirty();
2318 : auto newAttr(std::make_shared<VRTAttribute>(
2319 57 : GetFullName(), osName, anDimensions.empty() ? 0 : anDimensions[0],
2320 114 : oDataType));
2321 57 : m_oMapAttributes[osName] = newAttr;
2322 57 : return newAttr;
2323 : }
2324 :
2325 : /************************************************************************/
2326 : /* CopyFrom() */
2327 : /************************************************************************/
2328 :
2329 2 : bool VRTMDArray::CopyFrom(GDALDataset *poSrcDS, const GDALMDArray *poSrcArray,
2330 : bool bStrict, GUInt64 &nCurCost,
2331 : const GUInt64 nTotalCost,
2332 : GDALProgressFunc pfnProgress, void *pProgressData)
2333 : {
2334 2 : if (pfnProgress == nullptr)
2335 0 : pfnProgress = GDALDummyProgress;
2336 :
2337 2 : nCurCost += GDALMDArray::COPY_COST;
2338 :
2339 2 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
2340 : pfnProgress, pProgressData))
2341 : {
2342 0 : return false;
2343 : }
2344 :
2345 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
2346 :
2347 2 : if (poSrcDS)
2348 : {
2349 2 : const auto nDims(GetDimensionCount());
2350 4 : if (nDims == 1 && m_dims[0]->GetSize() > 2 &&
2351 2 : m_dims[0]->GetSize() < 10 * 1000 * 1000)
2352 : {
2353 : std::vector<double> adfTmp(
2354 4 : static_cast<size_t>(m_dims[0]->GetSize()));
2355 2 : const GUInt64 anStart[] = {0};
2356 2 : const size_t nCount = adfTmp.size();
2357 2 : const size_t anCount[] = {nCount};
2358 2 : if (poSrcArray->Read(anStart, anCount, nullptr, nullptr,
2359 4 : GDALExtendedDataType::Create(GDT_Float64),
2360 2 : &adfTmp[0]))
2361 : {
2362 2 : bool bRegular = true;
2363 : const double dfSpacing =
2364 2 : (adfTmp.back() - adfTmp[0]) / (nCount - 1);
2365 6 : for (size_t i = 1; i < nCount; i++)
2366 : {
2367 4 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfSpacing) >
2368 4 : 1e-3 * fabs(dfSpacing))
2369 : {
2370 0 : bRegular = false;
2371 0 : break;
2372 : }
2373 : }
2374 2 : if (bRegular)
2375 : {
2376 : std::unique_ptr<VRTMDArraySourceRegularlySpaced> poSource(
2377 2 : new VRTMDArraySourceRegularlySpaced(adfTmp[0],
2378 2 : dfSpacing));
2379 2 : AddSource(std::move(poSource));
2380 : }
2381 : }
2382 : }
2383 :
2384 2 : if (m_sources.empty())
2385 : {
2386 0 : std::vector<GUInt64> anSrcOffset(nDims);
2387 0 : std::vector<GUInt64> anCount(nDims);
2388 0 : std::vector<GUInt64> anStep(nDims, 1);
2389 0 : std::vector<GUInt64> anDstOffset(nDims);
2390 0 : for (size_t i = 0; i < nDims; i++)
2391 0 : anCount[i] = m_dims[i]->GetSize();
2392 :
2393 : std::unique_ptr<VRTMDArraySource> poSource(
2394 : new VRTMDArraySourceFromArray(
2395 0 : this, false, false, poSrcDS->GetDescription(),
2396 0 : poSrcArray->GetFullName(),
2397 0 : std::string(), // osBand
2398 0 : std::vector<int>(), // anTransposedAxis,
2399 0 : std::string(), // osViewExpr
2400 0 : std::move(anSrcOffset), std::move(anCount),
2401 0 : std::move(anStep), std::move(anDstOffset)));
2402 0 : AddSource(std::move(poSource));
2403 : }
2404 : }
2405 :
2406 2 : return true;
2407 : }
2408 :
2409 : /************************************************************************/
2410 : /* GetRawNoDataValue() */
2411 : /************************************************************************/
2412 :
2413 581 : const void *VRTMDArray::GetRawNoDataValue() const
2414 : {
2415 581 : return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
2416 : }
2417 :
2418 : /************************************************************************/
2419 : /* SetRawNoDataValue() */
2420 : /************************************************************************/
2421 :
2422 7 : bool VRTMDArray::SetRawNoDataValue(const void *pNoData)
2423 : {
2424 7 : SetDirty();
2425 :
2426 7 : if (!m_abyNoData.empty())
2427 : {
2428 0 : m_dt.FreeDynamicMemory(&m_abyNoData[0]);
2429 : }
2430 :
2431 7 : if (pNoData == nullptr)
2432 : {
2433 0 : m_abyNoData.clear();
2434 : }
2435 : else
2436 : {
2437 7 : const auto nSize = m_dt.GetSize();
2438 7 : m_abyNoData.resize(nSize);
2439 7 : memset(&m_abyNoData[0], 0, nSize);
2440 7 : GDALExtendedDataType::CopyValue(pNoData, m_dt, &m_abyNoData[0], m_dt);
2441 : }
2442 7 : return true;
2443 : }
2444 :
2445 : /************************************************************************/
2446 : /* SetSpatialRef() */
2447 : /************************************************************************/
2448 :
2449 1018 : bool VRTMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
2450 : {
2451 1018 : SetDirty();
2452 :
2453 1018 : m_poSRS.reset();
2454 1018 : if (poSRS)
2455 : {
2456 6 : m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
2457 : }
2458 1018 : return true;
2459 : }
2460 :
2461 : /************************************************************************/
2462 : /* AddSource() */
2463 : /************************************************************************/
2464 :
2465 1127 : void VRTMDArray::AddSource(std::unique_ptr<VRTMDArraySource> &&poSource)
2466 : {
2467 1127 : SetDirty();
2468 :
2469 1127 : m_sources.emplace_back(std::move(poSource));
2470 1127 : }
2471 :
2472 : /************************************************************************/
2473 : /* Serialize() */
2474 : /************************************************************************/
2475 :
2476 152 : void VRTMDArray::Serialize(CPLXMLNode *psParent, const char *pszVRTPath) const
2477 : {
2478 152 : CPLXMLNode *psArray = CPLCreateXMLNode(psParent, CXT_Element, "Array");
2479 152 : CPLAddXMLAttributeAndValue(psArray, "name", GetName().c_str());
2480 152 : CPLXMLNode *psDataType = CPLCreateXMLNode(psArray, CXT_Element, "DataType");
2481 152 : if (m_dt.GetClass() == GEDTC_STRING)
2482 52 : CPLCreateXMLNode(psDataType, CXT_Text, "String");
2483 : else
2484 100 : CPLCreateXMLNode(psDataType, CXT_Text,
2485 : GDALGetDataTypeName(m_dt.GetNumericDataType()));
2486 337 : for (const auto &dim : m_dims)
2487 : {
2488 370 : auto vrtDim(std::dynamic_pointer_cast<VRTDimension>(dim));
2489 185 : CPLAssert(vrtDim);
2490 185 : auto poGroup(GetGroup());
2491 185 : bool bSerializeDim = true;
2492 185 : if (poGroup)
2493 : {
2494 : auto groupDim(
2495 370 : poGroup->GetDimensionFromFullName(dim->GetFullName(), false));
2496 185 : if (groupDim && groupDim->GetSize() == dim->GetSize())
2497 : {
2498 184 : bSerializeDim = false;
2499 184 : CPLAssert(groupDim->GetGroup());
2500 : CPLXMLNode *psDimRef =
2501 184 : CPLCreateXMLNode(psArray, CXT_Element, "DimensionRef");
2502 184 : CPLAddXMLAttributeAndValue(psDimRef, "ref",
2503 184 : groupDim->GetGroup() == poGroup
2504 167 : ? dim->GetName().c_str()
2505 17 : : dim->GetFullName().c_str());
2506 : }
2507 : }
2508 185 : if (bSerializeDim)
2509 : {
2510 1 : vrtDim->Serialize(psArray);
2511 : }
2512 : }
2513 :
2514 152 : if (m_poSRS && !m_poSRS->IsEmpty())
2515 : {
2516 1 : char *pszWKT = nullptr;
2517 1 : const char *const apszOptions[2] = {"FORMAT=WKT2_2018", nullptr};
2518 1 : m_poSRS->exportToWkt(&pszWKT, apszOptions);
2519 : CPLXMLNode *psSRSNode =
2520 1 : CPLCreateXMLElementAndValue(psArray, "SRS", pszWKT);
2521 1 : CPLFree(pszWKT);
2522 1 : const auto &mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
2523 2 : CPLString osMapping;
2524 3 : for (size_t i = 0; i < mapping.size(); ++i)
2525 : {
2526 2 : if (!osMapping.empty())
2527 1 : osMapping += ",";
2528 2 : osMapping += CPLSPrintf("%d", mapping[i]);
2529 : }
2530 1 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
2531 : osMapping.c_str());
2532 : }
2533 :
2534 152 : if (!m_osUnit.empty())
2535 : {
2536 1 : CPLCreateXMLElementAndValue(psArray, "Unit", m_osUnit.c_str());
2537 : }
2538 :
2539 152 : bool bHasNodata = false;
2540 152 : double dfNoDataValue = GetNoDataValueAsDouble(&bHasNodata);
2541 152 : if (bHasNodata)
2542 : {
2543 1 : CPLSetXMLValue(
2544 : psArray, "NoDataValue",
2545 2 : VRTSerializeNoData(dfNoDataValue, m_dt.GetNumericDataType(), 18)
2546 : .c_str());
2547 : }
2548 :
2549 152 : if (m_bHasOffset)
2550 : {
2551 1 : CPLCreateXMLElementAndValue(psArray, "Offset",
2552 1 : CPLSPrintf("%.17g", m_dfOffset));
2553 : }
2554 :
2555 152 : if (m_bHasScale)
2556 : {
2557 1 : CPLCreateXMLElementAndValue(psArray, "Scale",
2558 1 : CPLSPrintf("%.17g", m_dfScale));
2559 : }
2560 :
2561 305 : for (const auto &poSource : m_sources)
2562 : {
2563 153 : poSource->Serialize(psArray, pszVRTPath);
2564 : }
2565 :
2566 197 : for (const auto &iter : m_oMapAttributes)
2567 : {
2568 45 : iter.second->Serialize(psArray);
2569 : }
2570 152 : }
2571 :
2572 : /************************************************************************/
2573 : /* VRTArraySource() */
2574 : /************************************************************************/
2575 :
2576 : class VRTArraySource : public VRTSource
2577 : {
2578 : std::unique_ptr<CPLXMLNode, CPLXMLTreeCloserDeleter> m_poXMLTree{};
2579 : std::unique_ptr<GDALDataset> m_poDS{};
2580 : std::unique_ptr<VRTSimpleSource> m_poSimpleSource{};
2581 :
2582 : public:
2583 32 : VRTArraySource() = default;
2584 :
2585 : CPLErr RasterIO(GDALDataType eBandDataType, int nXOff, int nYOff,
2586 : int nXSize, int nYSize, void *pData, int nBufXSize,
2587 : int nBufYSize, GDALDataType eBufType, GSpacing nPixelSpace,
2588 : GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
2589 : WorkingState &oWorkingState) override;
2590 :
2591 0 : double GetMinimum(int nXSize, int nYSize, int *pbSuccess) override
2592 : {
2593 0 : return m_poSimpleSource->GetMinimum(nXSize, nYSize, pbSuccess);
2594 : }
2595 :
2596 0 : double GetMaximum(int nXSize, int nYSize, int *pbSuccess) override
2597 : {
2598 0 : return m_poSimpleSource->GetMaximum(nXSize, nYSize, pbSuccess);
2599 : }
2600 :
2601 1 : CPLErr GetHistogram(int nXSize, int nYSize, double dfMin, double dfMax,
2602 : int nBuckets, GUIntBig *panHistogram,
2603 : int bIncludeOutOfRange, int bApproxOK,
2604 : GDALProgressFunc pfnProgress,
2605 : void *pProgressData) override
2606 : {
2607 2 : return m_poSimpleSource->GetHistogram(
2608 : nXSize, nYSize, dfMin, dfMax, nBuckets, panHistogram,
2609 1 : bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
2610 : }
2611 :
2612 0 : const char *GetType() const override
2613 : {
2614 0 : return "ArraySource";
2615 : }
2616 :
2617 : CPLErr XMLInit(const CPLXMLNode *psTree, const char *pszVRTPath,
2618 : VRTMapSharedResources &oMapSharedSources) override;
2619 : CPLXMLNode *SerializeToXML(const char *pszVRTPath) override;
2620 : };
2621 :
2622 : /************************************************************************/
2623 : /* RasterIO() */
2624 : /************************************************************************/
2625 :
2626 14 : CPLErr VRTArraySource::RasterIO(GDALDataType eBandDataType, int nXOff,
2627 : int nYOff, int nXSize, int nYSize, void *pData,
2628 : int nBufXSize, int nBufYSize,
2629 : GDALDataType eBufType, GSpacing nPixelSpace,
2630 : GSpacing nLineSpace,
2631 : GDALRasterIOExtraArg *psExtraArg,
2632 : WorkingState &oWorkingState)
2633 : {
2634 28 : return m_poSimpleSource->RasterIO(eBandDataType, nXOff, nYOff, nXSize,
2635 : nYSize, pData, nBufXSize, nBufYSize,
2636 : eBufType, nPixelSpace, nLineSpace,
2637 14 : psExtraArg, oWorkingState);
2638 : }
2639 :
2640 : /************************************************************************/
2641 : /* ParseSingleSourceArray() */
2642 : /************************************************************************/
2643 :
2644 : static std::shared_ptr<GDALMDArray>
2645 30 : ParseSingleSourceArray(const CPLXMLNode *psSingleSourceArray,
2646 : const char *pszVRTPath)
2647 : {
2648 : const auto psSourceFileNameNode =
2649 30 : CPLGetXMLNode(psSingleSourceArray, "SourceFilename");
2650 30 : if (!psSourceFileNameNode)
2651 : {
2652 1 : CPLError(CE_Failure, CPLE_AppDefined,
2653 : "Cannot find <SourceFilename> in <SingleSourceArray>");
2654 1 : return nullptr;
2655 : }
2656 : const char *pszSourceFilename =
2657 29 : CPLGetXMLValue(psSourceFileNameNode, nullptr, "");
2658 29 : const bool bRelativeToVRT = CPL_TO_BOOL(
2659 : atoi(CPLGetXMLValue(psSourceFileNameNode, "relativeToVRT", "0")));
2660 :
2661 : const char *pszSourceArray =
2662 29 : CPLGetXMLValue(psSingleSourceArray, "SourceArray", nullptr);
2663 29 : if (!pszSourceArray)
2664 : {
2665 1 : CPLError(CE_Failure, CPLE_AppDefined,
2666 : "Cannot find <SourceArray> in <SingleSourceArray>");
2667 1 : return nullptr;
2668 : }
2669 : const std::string osSourceFilename(
2670 : bRelativeToVRT
2671 : ? CPLProjectRelativeFilenameSafe(pszVRTPath, pszSourceFilename)
2672 56 : : std::string(pszSourceFilename));
2673 : auto poDS = std::unique_ptr<GDALDataset>(
2674 : GDALDataset::Open(osSourceFilename.c_str(),
2675 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_VERBOSE_ERROR,
2676 56 : nullptr, nullptr, nullptr));
2677 28 : if (!poDS)
2678 1 : return nullptr;
2679 54 : auto poRG = poDS->GetRootGroup();
2680 27 : if (!poRG)
2681 0 : return nullptr;
2682 81 : auto poArray = poRG->OpenMDArrayFromFullname(pszSourceArray);
2683 27 : if (!poArray)
2684 : {
2685 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array '%s' in %s",
2686 : pszSourceArray, osSourceFilename.c_str());
2687 : }
2688 27 : return poArray;
2689 : }
2690 :
2691 : /************************************************************************/
2692 : /* XMLInit() */
2693 : /************************************************************************/
2694 :
2695 32 : CPLErr VRTArraySource::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPath,
2696 : VRTMapSharedResources & /*oMapSharedSources*/)
2697 : {
2698 64 : const auto poArray = ParseArray(psTree, pszVRTPath, "ArraySource");
2699 32 : if (!poArray)
2700 : {
2701 17 : return CE_Failure;
2702 : }
2703 15 : if (poArray->GetDimensionCount() != 2)
2704 : {
2705 1 : CPLError(CE_Failure, CPLE_NotSupported,
2706 : "Array referenced in <ArraySource> should be a "
2707 : "two-dimensional array");
2708 1 : return CE_Failure;
2709 : }
2710 :
2711 14 : m_poDS.reset(poArray->AsClassicDataset(1, 0));
2712 14 : if (!m_poDS)
2713 0 : return CE_Failure;
2714 :
2715 14 : m_poSimpleSource = std::make_unique<VRTSimpleSource>();
2716 14 : auto poBand = m_poDS->GetRasterBand(1);
2717 14 : m_poSimpleSource->SetSrcBand(poBand);
2718 14 : m_poDS->Reference();
2719 :
2720 14 : if (m_poSimpleSource->ParseSrcRectAndDstRect(psTree) != CE_None)
2721 0 : return CE_Failure;
2722 14 : if (!CPLGetXMLNode(psTree, "SrcRect"))
2723 26 : m_poSimpleSource->SetSrcWindow(0, 0, poBand->GetXSize(),
2724 13 : poBand->GetYSize());
2725 14 : if (!CPLGetXMLNode(psTree, "DstRect"))
2726 26 : m_poSimpleSource->SetDstWindow(0, 0, poBand->GetXSize(),
2727 13 : poBand->GetYSize());
2728 :
2729 14 : m_poXMLTree.reset(CPLCloneXMLTree(psTree));
2730 14 : return CE_None;
2731 : }
2732 :
2733 : /************************************************************************/
2734 : /* SerializeToXML() */
2735 : /************************************************************************/
2736 :
2737 1 : CPLXMLNode *VRTArraySource::SerializeToXML(const char * /*pszVRTPath*/)
2738 : {
2739 1 : if (m_poXMLTree)
2740 : {
2741 1 : return CPLCloneXMLTree(m_poXMLTree.get());
2742 : }
2743 : else
2744 : {
2745 0 : CPLError(CE_Failure, CPLE_NotSupported,
2746 : "VRTArraySource::SerializeToXML() not implemented");
2747 0 : return nullptr;
2748 : }
2749 : }
2750 :
2751 : /************************************************************************/
2752 : /* VRTDerivedArrayCreate() */
2753 : /************************************************************************/
2754 :
2755 21 : std::shared_ptr<GDALMDArray> VRTDerivedArrayCreate(const char *pszVRTPath,
2756 : const CPLXMLNode *psTree)
2757 : {
2758 42 : auto poArray = ParseArray(psTree, pszVRTPath, "DerivedArray");
2759 :
2760 : const auto GetOptions =
2761 7 : [](const CPLXMLNode *psParent, CPLStringList &aosOptions)
2762 : {
2763 7 : for (const CPLXMLNode *psOption = CPLGetXMLNode(psParent, "Option");
2764 8 : psOption; psOption = psOption->psNext)
2765 : {
2766 4 : if (psOption->eType == CXT_Element &&
2767 4 : strcmp(psOption->pszValue, "Option") == 0)
2768 : {
2769 4 : const char *pszName = CPLGetXMLValue(psOption, "name", nullptr);
2770 4 : if (!pszName)
2771 : {
2772 3 : CPLError(
2773 : CE_Failure, CPLE_AppDefined,
2774 : "Cannot find 'name' attribute in <Option> element");
2775 3 : return false;
2776 : }
2777 1 : const char *pszValue = CPLGetXMLValue(psOption, nullptr, "");
2778 1 : aosOptions.SetNameValue(pszName, pszValue);
2779 : }
2780 : }
2781 4 : return true;
2782 : };
2783 :
2784 21 : for (const CPLXMLNode *psStep = CPLGetXMLNode(psTree, "Step");
2785 30 : psStep && poArray; psStep = psStep->psNext)
2786 : {
2787 20 : if (psStep->eType != CXT_Element ||
2788 20 : strcmp(psStep->pszValue, "Step") != 0)
2789 0 : continue;
2790 :
2791 20 : if (const CPLXMLNode *psView = CPLGetXMLNode(psStep, "View"))
2792 : {
2793 4 : const char *pszExpr = CPLGetXMLValue(psView, "expr", nullptr);
2794 4 : if (!pszExpr)
2795 : {
2796 1 : CPLError(CE_Failure, CPLE_AppDefined,
2797 : "Cannot find 'expr' attribute in <View> element");
2798 1 : return nullptr;
2799 : }
2800 3 : poArray = poArray->GetView(pszExpr);
2801 : }
2802 16 : else if (const CPLXMLNode *psTranspose =
2803 16 : CPLGetXMLNode(psStep, "Transpose"))
2804 : {
2805 : const char *pszOrder =
2806 2 : CPLGetXMLValue(psTranspose, "newOrder", nullptr);
2807 2 : if (!pszOrder)
2808 : {
2809 1 : CPLError(
2810 : CE_Failure, CPLE_AppDefined,
2811 : "Cannot find 'newOrder' attribute in <Transpose> element");
2812 1 : return nullptr;
2813 : }
2814 2 : std::vector<int> anMapNewAxisToOldAxis;
2815 1 : const CPLStringList aosItems(CSLTokenizeString2(pszOrder, ",", 0));
2816 3 : for (int i = 0; i < aosItems.size(); ++i)
2817 2 : anMapNewAxisToOldAxis.push_back(atoi(aosItems[i]));
2818 1 : poArray = poArray->Transpose(anMapNewAxisToOldAxis);
2819 : }
2820 14 : else if (const CPLXMLNode *psResample =
2821 14 : CPLGetXMLNode(psStep, "Resample"))
2822 : {
2823 5 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
2824 : auto poDummyGroup = std::shared_ptr<VRTGroup>(
2825 5 : new VRTGroup(pszVRTPath ? pszVRTPath : ""));
2826 5 : for (const CPLXMLNode *psDimension =
2827 5 : CPLGetXMLNode(psResample, "Dimension");
2828 10 : psDimension; psDimension = psDimension->psNext)
2829 : {
2830 6 : if (psDimension->eType == CXT_Element &&
2831 6 : strcmp(psDimension->pszValue, "Dimension") == 0)
2832 : {
2833 : auto apoDim = VRTDimension::Create(
2834 3 : poDummyGroup, std::string(), psDimension);
2835 3 : if (!apoDim)
2836 1 : return nullptr;
2837 2 : apoNewDims.emplace_back(std::move(apoDim));
2838 : }
2839 : }
2840 4 : if (apoNewDims.empty())
2841 3 : apoNewDims.resize(poArray->GetDimensionCount());
2842 :
2843 : const char *pszResampleAlg =
2844 4 : CPLGetXMLValue(psResample, "ResampleAlg", "NEAR");
2845 : const auto eResampleAlg =
2846 4 : GDALRasterIOGetResampleAlg(pszResampleAlg);
2847 :
2848 0 : std::unique_ptr<OGRSpatialReference> poSRS;
2849 4 : const char *pszSRS = CPLGetXMLValue(psResample, "SRS", nullptr);
2850 4 : if (pszSRS)
2851 : {
2852 2 : poSRS = std::make_unique<OGRSpatialReference>();
2853 2 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2854 2 : if (poSRS->SetFromUserInput(
2855 : pszSRS, OGRSpatialReference::
2856 2 : SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
2857 : OGRERR_NONE)
2858 : {
2859 1 : CPLError(CE_Failure, CPLE_AppDefined,
2860 : "Invalid value for <SRS>");
2861 1 : return nullptr;
2862 : }
2863 : }
2864 :
2865 3 : CPLStringList aosOptions;
2866 3 : if (!GetOptions(psResample, aosOptions))
2867 1 : return nullptr;
2868 :
2869 6 : poArray = poArray->GetResampled(apoNewDims, eResampleAlg,
2870 4 : poSRS.get(), aosOptions.List());
2871 : }
2872 9 : else if (const CPLXMLNode *psGrid = CPLGetXMLNode(psStep, "Grid"))
2873 : {
2874 : const char *pszGridOptions =
2875 5 : CPLGetXMLValue(psGrid, "GridOptions", nullptr);
2876 5 : if (!pszGridOptions)
2877 : {
2878 1 : CPLError(CE_Failure, CPLE_AppDefined,
2879 : "Cannot find <GridOptions> in <Grid> element");
2880 4 : return nullptr;
2881 : }
2882 :
2883 0 : std::shared_ptr<GDALMDArray> poXArray;
2884 4 : if (const CPLXMLNode *psXArrayNode =
2885 4 : CPLGetXMLNode(psGrid, "XArray"))
2886 : {
2887 2 : poXArray = ParseArray(psXArrayNode, pszVRTPath, "XArray");
2888 2 : if (!poXArray)
2889 1 : return nullptr;
2890 : }
2891 :
2892 0 : std::shared_ptr<GDALMDArray> poYArray;
2893 3 : if (const CPLXMLNode *psYArrayNode =
2894 3 : CPLGetXMLNode(psGrid, "YArray"))
2895 : {
2896 2 : poYArray = ParseArray(psYArrayNode, pszVRTPath, "YArray");
2897 2 : if (!poYArray)
2898 1 : return nullptr;
2899 : }
2900 :
2901 2 : CPLStringList aosOptions;
2902 2 : if (!GetOptions(psGrid, aosOptions))
2903 1 : return nullptr;
2904 :
2905 3 : poArray = poArray->GetGridded(pszGridOptions, poXArray, poYArray,
2906 2 : aosOptions.List());
2907 : }
2908 4 : else if (const CPLXMLNode *psGetMask = CPLGetXMLNode(psStep, "GetMask"))
2909 : {
2910 2 : CPLStringList aosOptions;
2911 2 : if (!GetOptions(psGetMask, aosOptions))
2912 1 : return nullptr;
2913 :
2914 1 : poArray = poArray->GetMask(aosOptions.List());
2915 : }
2916 2 : else if (CPLGetXMLNode(psStep, "GetUnscaled"))
2917 : {
2918 1 : poArray = poArray->GetUnscaled();
2919 : }
2920 : else
2921 : {
2922 1 : CPLError(CE_Failure, CPLE_NotSupported,
2923 : "Unknown <Step>.<%s> element",
2924 1 : psStep->psChild ? psStep->psChild->pszValue : "(null)");
2925 1 : return nullptr;
2926 : }
2927 : }
2928 :
2929 10 : return poArray;
2930 : }
2931 :
2932 : /************************************************************************/
2933 : /* ParseArray() */
2934 : /************************************************************************/
2935 :
2936 57 : static std::shared_ptr<GDALMDArray> ParseArray(const CPLXMLNode *psTree,
2937 : const char *pszVRTPath,
2938 : const char *pszParentXMLNode)
2939 : {
2940 57 : if (const CPLXMLNode *psSingleSourceArrayNode =
2941 57 : CPLGetXMLNode(psTree, "SingleSourceArray"))
2942 30 : return ParseSingleSourceArray(psSingleSourceArrayNode, pszVRTPath);
2943 :
2944 27 : if (const CPLXMLNode *psArrayNode = CPLGetXMLNode(psTree, "Array"))
2945 : {
2946 2 : return VRTMDArray::Create(pszVRTPath, psArrayNode);
2947 : }
2948 :
2949 25 : if (const CPLXMLNode *psDerivedArrayNode =
2950 25 : CPLGetXMLNode(psTree, "DerivedArray"))
2951 : {
2952 21 : return VRTDerivedArrayCreate(pszVRTPath, psDerivedArrayNode);
2953 : }
2954 :
2955 4 : CPLError(
2956 : CE_Failure, CPLE_AppDefined,
2957 : "Cannot find a <SimpleSourceArray>, <Array> or <DerivedArray> in <%s>",
2958 : pszParentXMLNode);
2959 4 : return nullptr;
2960 : }
2961 :
2962 : /************************************************************************/
2963 : /* VRTParseArraySource() */
2964 : /************************************************************************/
2965 :
2966 32 : VRTSource *VRTParseArraySource(const CPLXMLNode *psChild,
2967 : const char *pszVRTPath,
2968 : VRTMapSharedResources &oMapSharedSources)
2969 : {
2970 32 : VRTSource *poSource = nullptr;
2971 :
2972 32 : if (EQUAL(psChild->pszValue, "ArraySource"))
2973 : {
2974 32 : poSource = new VRTArraySource();
2975 : }
2976 : else
2977 : {
2978 0 : CPLError(CE_Failure, CPLE_AppDefined,
2979 : "VRTParseArraySource() - Unknown source : %s",
2980 0 : psChild->pszValue);
2981 0 : return nullptr;
2982 : }
2983 :
2984 32 : if (poSource->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
2985 14 : return poSource;
2986 :
2987 18 : delete poSource;
2988 18 : return nullptr;
2989 : }
2990 :
2991 : /*! @endcond */
|