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