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