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