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