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