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