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