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