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