Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTProcessedDataset.
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_minixml.h"
14 : #include "cpl_string.h"
15 : #include "gdal_utils.h"
16 : #include "vrtdataset.h"
17 :
18 : #include <algorithm>
19 : #include <limits>
20 : #include <map>
21 : #include <vector>
22 :
23 : /************************************************************************/
24 : /* VRTProcessedDatasetFunc */
25 : /************************************************************************/
26 :
27 : //! Structure holding information for a VRTProcessedDataset function.
28 : struct VRTProcessedDatasetFunc
29 : {
30 : //! Processing function name
31 : std::string osFuncName{};
32 :
33 : //! User data to provide to pfnInit, pfnFree, pfnProcess callbacks.
34 : void *pUserData = nullptr;
35 :
36 : //! Whether XML metadata has been specified
37 : bool bMetadataSpecified = false;
38 :
39 : //! Map of (constant argument name, constant value)
40 : std::map<std::string, std::string> oMapConstantArguments{};
41 :
42 : //! Set of builtin argument names (e.g "offset", "scale", "nodata")
43 : std::set<std::string> oSetBuiltinArguments{};
44 :
45 : //! Arguments defined in the VRT
46 : struct OtherArgument
47 : {
48 : std::string osType{};
49 : bool bRequired = false;
50 : };
51 :
52 : std::map<std::string, OtherArgument> oOtherArguments{};
53 :
54 : //! Requested input data type.
55 : GDALDataType eRequestedInputDT = GDT_Unknown;
56 :
57 : //! List of supported input datatypes. Empty if no restriction.
58 : std::vector<GDALDataType> aeSupportedInputDT{};
59 :
60 : //! List of supported input band counts. Empty if no restriction.
61 : std::vector<int> anSupportedInputBandCount{};
62 :
63 : //! Optional initialization function
64 : GDALVRTProcessedDatasetFuncInit pfnInit = nullptr;
65 :
66 : //! Optional free function
67 : GDALVRTProcessedDatasetFuncFree pfnFree = nullptr;
68 :
69 : //! Required processing function
70 : GDALVRTProcessedDatasetFuncProcess pfnProcess = nullptr;
71 : };
72 :
73 : /************************************************************************/
74 : /* GetGlobalMapProcessedDatasetFunc() */
75 : /************************************************************************/
76 :
77 : /** Return the registry of VRTProcessedDatasetFunc functions */
78 : static std::map<std::string, VRTProcessedDatasetFunc> &
79 7479 : GetGlobalMapProcessedDatasetFunc()
80 : {
81 7479 : static std::map<std::string, VRTProcessedDatasetFunc> goMap;
82 7479 : return goMap;
83 : }
84 :
85 : /************************************************************************/
86 : /* Step::~Step() */
87 : /************************************************************************/
88 :
89 : /*! @cond Doxygen_Suppress */
90 :
91 : /** Step destructor */
92 150 : VRTProcessedDataset::Step::~Step()
93 : {
94 150 : deinit();
95 150 : }
96 :
97 : /************************************************************************/
98 : /* Step::deinit() */
99 : /************************************************************************/
100 :
101 : /** Free pWorkingData */
102 150 : void VRTProcessedDataset::Step::deinit()
103 : {
104 150 : if (pWorkingData)
105 : {
106 58 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
107 58 : const auto oIterFunc = oMapFunctions.find(osAlgorithm);
108 58 : if (oIterFunc != oMapFunctions.end())
109 : {
110 58 : if (oIterFunc->second.pfnFree)
111 : {
112 116 : oIterFunc->second.pfnFree(osAlgorithm.c_str(),
113 58 : oIterFunc->second.pUserData,
114 : pWorkingData);
115 : }
116 : }
117 : else
118 : {
119 0 : CPLAssert(false);
120 : }
121 58 : pWorkingData = nullptr;
122 : }
123 150 : }
124 :
125 : /************************************************************************/
126 : /* Step::Step(Step&& other) */
127 : /************************************************************************/
128 :
129 : /** Move constructor */
130 61 : VRTProcessedDataset::Step::Step(Step &&other)
131 61 : : osAlgorithm(std::move(other.osAlgorithm)),
132 122 : aosArguments(std::move(other.aosArguments)), eInDT(other.eInDT),
133 61 : eOutDT(other.eOutDT), nInBands(other.nInBands),
134 61 : nOutBands(other.nOutBands), adfInNoData(other.adfInNoData),
135 61 : adfOutNoData(other.adfOutNoData), pWorkingData(other.pWorkingData)
136 : {
137 61 : other.pWorkingData = nullptr;
138 61 : }
139 :
140 : /************************************************************************/
141 : /* Step operator=(Step&& other) */
142 : /************************************************************************/
143 :
144 : /** Move assignment operator */
145 0 : VRTProcessedDataset::Step &VRTProcessedDataset::Step::operator=(Step &&other)
146 : {
147 0 : if (&other != this)
148 : {
149 0 : deinit();
150 0 : osAlgorithm = std::move(other.osAlgorithm);
151 0 : aosArguments = std::move(other.aosArguments);
152 0 : eInDT = other.eInDT;
153 0 : eOutDT = other.eOutDT;
154 0 : nInBands = other.nInBands;
155 0 : nOutBands = other.nOutBands;
156 0 : adfInNoData = std::move(other.adfInNoData);
157 0 : adfOutNoData = std::move(other.adfOutNoData);
158 0 : std::swap(pWorkingData, other.pWorkingData);
159 : }
160 0 : return *this;
161 : }
162 :
163 : /************************************************************************/
164 : /* VRTProcessedDataset() */
165 : /************************************************************************/
166 :
167 : /** Constructor */
168 99 : VRTProcessedDataset::VRTProcessedDataset(int nXSize, int nYSize)
169 99 : : VRTDataset(nXSize, nYSize)
170 : {
171 99 : }
172 :
173 : /************************************************************************/
174 : /* ~VRTProcessedDataset() */
175 : /************************************************************************/
176 :
177 198 : VRTProcessedDataset::~VRTProcessedDataset()
178 :
179 : {
180 99 : VRTProcessedDataset::FlushCache(true);
181 99 : VRTProcessedDataset::CloseDependentDatasets();
182 198 : }
183 :
184 : /************************************************************************/
185 : /* XMLInit() */
186 : /************************************************************************/
187 :
188 : /** Instantiate object from XML tree */
189 96 : CPLErr VRTProcessedDataset::XMLInit(const CPLXMLNode *psTree,
190 : const char *pszVRTPathIn)
191 :
192 : {
193 96 : if (Init(psTree, pszVRTPathIn, nullptr, nullptr, -1) != CE_None)
194 43 : return CE_Failure;
195 :
196 53 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
197 53 : const int nOvrCount = poSrcFirstBand->GetOverviewCount();
198 56 : for (int i = 0; i < nOvrCount; ++i)
199 : {
200 3 : auto poOvrDS = std::make_unique<VRTProcessedDataset>(0, 0);
201 3 : if (poOvrDS->Init(psTree, pszVRTPathIn, this, m_poSrcDS.get(), i) !=
202 : CE_None)
203 0 : break;
204 3 : m_apoOverviewDatasets.emplace_back(std::move(poOvrDS));
205 : }
206 :
207 53 : return CE_None;
208 : }
209 :
210 84 : static bool HasScaleOffset(GDALDataset &oSrcDS)
211 : {
212 372 : for (int i = 1; i <= oSrcDS.GetRasterCount(); i++)
213 : {
214 : int pbSuccess;
215 290 : GDALRasterBand &oBand = *oSrcDS.GetRasterBand(i);
216 290 : double scale = oBand.GetScale(&pbSuccess);
217 290 : if (pbSuccess && scale != 1)
218 : {
219 2 : return true;
220 : }
221 288 : double offset = oBand.GetOffset(&pbSuccess);
222 288 : if (pbSuccess && offset != 0)
223 : {
224 0 : return true;
225 : }
226 : }
227 :
228 82 : return false;
229 : }
230 :
231 : /** Instantiate object from XML tree */
232 99 : CPLErr VRTProcessedDataset::Init(const CPLXMLNode *psTree,
233 : const char *pszVRTPathIn,
234 : const VRTProcessedDataset *poParentDS,
235 : GDALDataset *poParentSrcDS, int iOvrLevel)
236 :
237 : {
238 99 : const CPLXMLNode *psInput = CPLGetXMLNode(psTree, "Input");
239 99 : if (!psInput)
240 : {
241 1 : CPLError(CE_Failure, CPLE_AppDefined, "Input element missing");
242 1 : return CE_Failure;
243 : }
244 :
245 98 : if (pszVRTPathIn)
246 12 : m_osVRTPath = pszVRTPathIn;
247 :
248 98 : if (poParentSrcDS)
249 : {
250 3 : m_poSrcDS.reset(
251 : GDALCreateOverviewDataset(poParentSrcDS, iOvrLevel, true));
252 : }
253 95 : else if (const CPLXMLNode *psSourceFileNameNode =
254 95 : CPLGetXMLNode(psInput, "SourceFilename"))
255 : {
256 93 : const bool bRelativeToVRT = CPL_TO_BOOL(
257 : atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0")));
258 : const std::string osFilename = GDALDataset::BuildFilename(
259 : CPLGetXMLValue(psInput, "SourceFilename", ""), pszVRTPathIn,
260 186 : bRelativeToVRT);
261 93 : m_poSrcDS.reset(GDALDataset::Open(
262 : osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
263 : nullptr, nullptr));
264 : }
265 2 : else if (const CPLXMLNode *psVRTDataset =
266 2 : CPLGetXMLNode(psInput, "VRTDataset"))
267 : {
268 1 : CPLXMLNode sVRTDatasetTmp = *psVRTDataset;
269 1 : sVRTDatasetTmp.psNext = nullptr;
270 1 : char *pszXML = CPLSerializeXMLTree(&sVRTDatasetTmp);
271 1 : m_poSrcDS = VRTDataset::OpenXML(pszXML, pszVRTPathIn, GA_ReadOnly);
272 1 : CPLFree(pszXML);
273 : }
274 : else
275 : {
276 1 : CPLError(
277 : CE_Failure, CPLE_AppDefined,
278 : "Input element should have a SourceFilename or VRTDataset element");
279 1 : return CE_Failure;
280 : }
281 :
282 97 : if (!m_poSrcDS)
283 2 : return CE_Failure;
284 :
285 95 : const char *pszUnscale = CPLGetXMLValue(psInput, "unscale", "AUTO");
286 95 : bool bUnscale = false;
287 95 : if (EQUAL(pszUnscale, "AUTO"))
288 : {
289 84 : if (HasScaleOffset(*m_poSrcDS))
290 : {
291 2 : bUnscale = true;
292 : }
293 : }
294 11 : else if (EQUAL(pszUnscale, "YES") || EQUAL(pszUnscale, "ON") ||
295 11 : EQUAL(pszUnscale, "TRUE") || EQUAL(pszUnscale, "1"))
296 : {
297 6 : bUnscale = true;
298 : }
299 5 : else if (!(EQUAL(pszUnscale, "NO") || EQUAL(pszUnscale, "OFF") ||
300 5 : EQUAL(pszUnscale, "FALSE") || EQUAL(pszUnscale, "0")))
301 : {
302 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value of 'unscale'");
303 1 : return CE_Failure;
304 : }
305 :
306 94 : if (bUnscale)
307 : {
308 8 : CPLStringList oArgs;
309 8 : oArgs.AddString("-unscale");
310 8 : oArgs.AddString("-ot");
311 8 : oArgs.AddString("Float64");
312 8 : oArgs.AddString("-of");
313 8 : oArgs.AddString("VRT");
314 8 : oArgs.AddString("-a_nodata");
315 8 : oArgs.AddString("nan");
316 8 : auto *poArgs = GDALTranslateOptionsNew(oArgs.List(), nullptr);
317 : int pbUsageError;
318 8 : CPLAssert(poArgs);
319 8 : m_poVRTSrcDS.reset(m_poSrcDS.release());
320 : // https://trac.cppcheck.net/ticket/11325
321 : // cppcheck-suppress accessMoved
322 8 : m_poSrcDS.reset(GDALDataset::FromHandle(
323 8 : GDALTranslate("", m_poVRTSrcDS.get(), poArgs, &pbUsageError)));
324 8 : GDALTranslateOptionsFree(poArgs);
325 :
326 8 : if (pbUsageError || !m_poSrcDS)
327 : {
328 0 : return CE_Failure;
329 : }
330 : }
331 :
332 94 : if (nRasterXSize == 0 && nRasterYSize == 0)
333 : {
334 92 : nRasterXSize = m_poSrcDS->GetRasterXSize();
335 92 : nRasterYSize = m_poSrcDS->GetRasterYSize();
336 : }
337 2 : else if (nRasterXSize != m_poSrcDS->GetRasterXSize() ||
338 0 : nRasterYSize != m_poSrcDS->GetRasterYSize())
339 : {
340 2 : CPLError(CE_Failure, CPLE_AppDefined,
341 : "Inconsistent declared VRT dimensions with input dataset");
342 2 : return CE_Failure;
343 : }
344 :
345 92 : if (m_poSrcDS->GetRasterCount() == 0)
346 0 : return CE_Failure;
347 :
348 : // Inherit SRS from source if not explicitly defined in VRT
349 92 : if (!CPLGetXMLNode(psTree, "SRS"))
350 : {
351 92 : const OGRSpatialReference *poSRS = m_poSrcDS->GetSpatialRef();
352 92 : if (poSRS)
353 : {
354 10 : m_poSRS.reset(poSRS->Clone());
355 : }
356 : }
357 :
358 : // Inherit GeoTransform from source if not explicitly defined in VRT
359 92 : if (iOvrLevel < 0 && !CPLGetXMLNode(psTree, "GeoTransform"))
360 : {
361 89 : if (m_poSrcDS->GetGeoTransform(m_gt) == CE_None)
362 52 : m_bGeoTransformSet = true;
363 : }
364 :
365 : /* -------------------------------------------------------------------- */
366 : /* Initialize blocksize before calling sub-init so that the */
367 : /* band initializers can get it from the dataset object when */
368 : /* they are created. */
369 : /* -------------------------------------------------------------------- */
370 :
371 92 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
372 92 : poSrcFirstBand->GetBlockSize(&m_nBlockXSize, &m_nBlockYSize);
373 92 : bool bUserBlockSize = false;
374 92 : if (const char *pszBlockXSize =
375 92 : CPLGetXMLValue(psTree, "BlockXSize", nullptr))
376 : {
377 0 : bUserBlockSize = true;
378 0 : m_nBlockXSize = atoi(pszBlockXSize);
379 0 : if (m_nBlockXSize <= 1)
380 : {
381 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockXSize");
382 0 : return CE_Failure;
383 : }
384 : }
385 92 : if (const char *pszBlockYSize =
386 92 : CPLGetXMLValue(psTree, "BlockYSize", nullptr))
387 : {
388 0 : bUserBlockSize = true;
389 0 : m_nBlockYSize = atoi(pszBlockYSize);
390 0 : if (m_nBlockYSize <= 1)
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockYSize");
393 0 : return CE_Failure;
394 : }
395 : }
396 :
397 : // Initialize all the general VRT stuff.
398 92 : if (VRTDataset::XMLInit(psTree, pszVRTPathIn) != CE_None)
399 : {
400 0 : return CE_Failure;
401 : }
402 :
403 : // Use geotransform from parent for overviews
404 92 : if (iOvrLevel >= 0 && poParentDS->m_bGeoTransformSet)
405 : {
406 1 : m_bGeoTransformSet = true;
407 1 : m_gt = poParentDS->m_gt;
408 2 : m_gt[1] *=
409 1 : static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
410 2 : m_gt[2] *=
411 1 : static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
412 2 : m_gt[4] *=
413 1 : static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
414 1 : m_gt[5] *=
415 1 : static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
416 : }
417 :
418 92 : const CPLXMLNode *psOutputBands = CPLGetXMLNode(psTree, "OutputBands");
419 92 : if (psOutputBands)
420 : {
421 14 : if (const char *pszCount =
422 14 : CPLGetXMLValue(psOutputBands, "count", nullptr))
423 : {
424 14 : if (EQUAL(pszCount, "FROM_LAST_STEP"))
425 : {
426 8 : m_outputBandCountProvenance = ValueProvenance::FROM_LAST_STEP;
427 : }
428 6 : else if (!EQUAL(pszCount, "FROM_SOURCE"))
429 : {
430 4 : if (CPLGetValueType(pszCount) == CPL_VALUE_INTEGER)
431 : {
432 3 : m_outputBandCountProvenance =
433 : ValueProvenance::USER_PROVIDED;
434 3 : m_outputBandCountValue = atoi(pszCount);
435 3 : if (!GDALCheckBandCount(m_outputBandCountValue,
436 : /* bIsZeroAllowed = */ false))
437 : {
438 : // Error emitted by above function
439 1 : return CE_Failure;
440 : }
441 : }
442 : else
443 : {
444 1 : CPLError(CE_Failure, CPLE_AppDefined,
445 : "Invalid value for OutputBands.count");
446 1 : return CE_Failure;
447 : }
448 : }
449 : }
450 :
451 12 : if (const char *pszType =
452 12 : CPLGetXMLValue(psOutputBands, "dataType", nullptr))
453 : {
454 12 : if (EQUAL(pszType, "FROM_LAST_STEP"))
455 : {
456 4 : m_outputBandDataTypeProvenance =
457 : ValueProvenance::FROM_LAST_STEP;
458 : }
459 8 : else if (!EQUAL(pszType, "FROM_SOURCE"))
460 : {
461 6 : m_outputBandDataTypeProvenance = ValueProvenance::USER_PROVIDED;
462 6 : m_outputBandDataTypeValue = GDALGetDataTypeByName(pszType);
463 6 : if (m_outputBandDataTypeValue == GDT_Unknown)
464 : {
465 1 : CPLError(CE_Failure, CPLE_AppDefined,
466 : "Invalid value for OutputBands.dataType");
467 1 : return CE_Failure;
468 : }
469 : }
470 : }
471 : }
472 78 : else if (CPLGetXMLNode(psTree, "VRTRasterBand"))
473 : {
474 17 : m_outputBandCountProvenance = ValueProvenance::FROM_VRTRASTERBAND;
475 17 : m_outputBandDataTypeProvenance = ValueProvenance::FROM_VRTRASTERBAND;
476 : }
477 :
478 89 : int nOutputBandCount = 0;
479 89 : switch (m_outputBandCountProvenance)
480 : {
481 1 : case ValueProvenance::USER_PROVIDED:
482 1 : nOutputBandCount = m_outputBandCountValue;
483 1 : break;
484 63 : case ValueProvenance::FROM_SOURCE:
485 63 : nOutputBandCount = m_poSrcDS->GetRasterCount();
486 63 : break;
487 17 : case ValueProvenance::FROM_VRTRASTERBAND:
488 17 : nOutputBandCount = nBands;
489 17 : break;
490 8 : case ValueProvenance::FROM_LAST_STEP:
491 8 : break;
492 : }
493 :
494 : const CPLXMLNode *psProcessingSteps =
495 89 : CPLGetXMLNode(psTree, "ProcessingSteps");
496 89 : if (!psProcessingSteps)
497 : {
498 1 : CPLError(CE_Failure, CPLE_AppDefined,
499 : "ProcessingSteps element missing");
500 1 : return CE_Failure;
501 : }
502 :
503 88 : const auto eInDT = poSrcFirstBand->GetRasterDataType();
504 298 : for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
505 : {
506 210 : const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
507 210 : if (eDT != eInDT)
508 : {
509 0 : CPLError(CE_Warning, CPLE_AppDefined,
510 : "Not all bands of the input dataset have the same data "
511 : "type. The data type of the first band will be used as "
512 : "the reference one.");
513 0 : break;
514 : }
515 : }
516 :
517 88 : GDALDataType eCurrentDT = eInDT;
518 88 : int nCurrentBandCount = m_poSrcDS->GetRasterCount();
519 :
520 176 : std::vector<double> adfNoData;
521 386 : for (int i = 1; i <= nCurrentBandCount; ++i)
522 : {
523 298 : int bHasVal = FALSE;
524 : const double dfVal =
525 298 : m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
526 : adfNoData.emplace_back(
527 298 : bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
528 : }
529 :
530 88 : int nStepCount = 0;
531 177 : for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
532 89 : psStep = psStep->psNext)
533 : {
534 89 : if (psStep->eType == CXT_Element &&
535 89 : strcmp(psStep->pszValue, "Step") == 0)
536 : {
537 89 : ++nStepCount;
538 : }
539 : }
540 :
541 88 : int iStep = 0;
542 146 : for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
543 58 : psStep = psStep->psNext)
544 : {
545 89 : if (psStep->eType == CXT_Element &&
546 89 : strcmp(psStep->pszValue, "Step") == 0)
547 : {
548 89 : ++iStep;
549 89 : const bool bIsFinalStep = (iStep == nStepCount);
550 89 : std::vector<double> adfOutNoData;
551 89 : if (bIsFinalStep)
552 : {
553 : // Initialize adfOutNoData with nodata value of *output* bands
554 : // for final step
555 87 : if (m_outputBandCountProvenance ==
556 : ValueProvenance::FROM_VRTRASTERBAND)
557 : {
558 48 : for (int i = 1; i <= nBands; ++i)
559 : {
560 31 : int bHasVal = FALSE;
561 : const double dfVal =
562 31 : GetRasterBand(i)->GetNoDataValue(&bHasVal);
563 : adfOutNoData.emplace_back(
564 31 : bHasVal ? dfVal
565 31 : : std::numeric_limits<double>::quiet_NaN());
566 : }
567 : }
568 70 : else if (m_outputBandCountProvenance ==
569 : ValueProvenance::FROM_SOURCE)
570 : {
571 213 : for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
572 : {
573 152 : int bHasVal = FALSE;
574 : const double dfVal =
575 152 : m_poSrcDS->GetRasterBand(i)->GetNoDataValue(
576 152 : &bHasVal);
577 : adfOutNoData.emplace_back(
578 182 : bHasVal ? dfVal
579 182 : : std::numeric_limits<double>::quiet_NaN());
580 : }
581 : }
582 9 : else if (m_outputBandCountProvenance ==
583 : ValueProvenance::USER_PROVIDED)
584 : {
585 1 : adfOutNoData.resize(
586 1 : m_outputBandCountValue,
587 1 : std::numeric_limits<double>::quiet_NaN());
588 : }
589 : }
590 89 : if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
591 : adfNoData, adfOutNoData))
592 31 : return CE_Failure;
593 58 : adfNoData = std::move(adfOutNoData);
594 : }
595 : }
596 :
597 57 : if (m_aoSteps.empty())
598 : {
599 1 : CPLError(CE_Failure, CPLE_AppDefined,
600 : "At least one step should be defined");
601 1 : return CE_Failure;
602 : }
603 :
604 56 : int nLargestInDTSizeTimesBand = 1;
605 56 : int nLargestOutDTSizeTimesBand = 1;
606 114 : for (const auto &oStep : m_aoSteps)
607 : {
608 : const int nInDTSizeTimesBand =
609 58 : GDALGetDataTypeSizeBytes(oStep.eInDT) * oStep.nInBands;
610 58 : nLargestInDTSizeTimesBand =
611 58 : std::max(nLargestInDTSizeTimesBand, nInDTSizeTimesBand);
612 : const int nOutDTSizeTimesBand =
613 58 : GDALGetDataTypeSizeBytes(oStep.eOutDT) * oStep.nOutBands;
614 58 : nLargestOutDTSizeTimesBand =
615 58 : std::max(nLargestOutDTSizeTimesBand, nOutDTSizeTimesBand);
616 : }
617 56 : m_nWorkingBytesPerPixel =
618 56 : nLargestInDTSizeTimesBand + nLargestOutDTSizeTimesBand;
619 :
620 : // Use only up to 40% of RAM to acquire source bands and generate the output
621 : // buffer.
622 56 : m_nAllowedRAMUsage = CPLGetUsablePhysicalRAM() / 10 * 4;
623 : // Only for tests now
624 56 : const char *pszMAX_RAM = "VRT_PROCESSED_DATASET_ALLOWED_RAM_USAGE";
625 56 : if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
626 : {
627 3 : CPL_IGNORE_RET_VAL(
628 3 : CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
629 : }
630 :
631 56 : if (m_nAllowedRAMUsage > 0)
632 : {
633 56 : bool bBlockSizeModified = false;
634 88 : while ((m_nBlockXSize >= 2 || m_nBlockYSize >= 2) &&
635 81 : static_cast<GIntBig>(m_nBlockXSize) * m_nBlockYSize >
636 81 : m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
637 : {
638 32 : if ((m_nBlockXSize == nRasterXSize ||
639 30 : m_nBlockYSize >= m_nBlockXSize) &&
640 17 : m_nBlockYSize >= 2)
641 : {
642 16 : m_nBlockYSize /= 2;
643 : }
644 : else
645 : {
646 16 : m_nBlockXSize /= 2;
647 : }
648 32 : bBlockSizeModified = true;
649 : }
650 56 : if (bBlockSizeModified)
651 : {
652 3 : if (bUserBlockSize)
653 : {
654 0 : CPLError(
655 : CE_Warning, CPLE_AppDefined,
656 : "Reducing block size to %d x %d to avoid consuming too "
657 : "much RAM",
658 : m_nBlockXSize, m_nBlockYSize);
659 : }
660 : else
661 : {
662 3 : CPLDebug(
663 : "VRT",
664 : "Reducing block size to %d x %d to avoid consuming too "
665 : "much RAM",
666 : m_nBlockXSize, m_nBlockYSize);
667 : }
668 : }
669 : }
670 :
671 56 : if (m_outputBandCountProvenance == ValueProvenance::FROM_LAST_STEP)
672 : {
673 7 : nOutputBandCount = nCurrentBandCount;
674 : }
675 49 : else if (nOutputBandCount != nCurrentBandCount)
676 : {
677 : // Should not happen frequently as pixel init functions are expected
678 : // to validate that they can accept the number of output bands provided
679 : // to them
680 0 : CPLError(
681 : CE_Failure, CPLE_AppDefined,
682 : "Number of output bands of last step (%d) is not consistent with "
683 : "number of VRTProcessedRasterBand's (%d)",
684 : nCurrentBandCount, nBands);
685 0 : return CE_Failure;
686 : }
687 :
688 56 : if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
689 : {
690 3 : m_outputBandDataTypeValue = eCurrentDT;
691 : }
692 :
693 34 : const auto ClearBands = [this]()
694 : {
695 9 : for (int i = 0; i < nBands; ++i)
696 1 : delete papoBands[i];
697 8 : CPLFree(papoBands);
698 8 : papoBands = nullptr;
699 8 : nBands = 0;
700 64 : };
701 :
702 73 : if (nBands != 0 &&
703 17 : (nBands != nOutputBandCount ||
704 16 : (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP &&
705 1 : m_outputBandDataTypeValue != papoBands[0]->GetRasterDataType())))
706 : {
707 1 : ClearBands();
708 : }
709 :
710 176 : const auto GetOutputBandType = [this, eCurrentDT](GDALDataType eSourceDT)
711 : {
712 87 : if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
713 3 : return eCurrentDT;
714 84 : else if (m_outputBandDataTypeProvenance ==
715 : ValueProvenance::USER_PROVIDED)
716 5 : return m_outputBandDataTypeValue;
717 : else
718 79 : return eSourceDT;
719 56 : };
720 :
721 56 : if (m_outputBandCountProvenance == ValueProvenance::FROM_SOURCE)
722 : {
723 112 : for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
724 : {
725 79 : const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
726 : const GDALDataType eOutputBandType =
727 79 : GetOutputBandType(poSrcBand->GetRasterDataType());
728 : auto poBand =
729 79 : new VRTProcessedRasterBand(this, i + 1, eOutputBandType);
730 79 : poBand->CopyCommonInfoFrom(poSrcBand);
731 79 : SetBand(i + 1, poBand);
732 : }
733 : }
734 23 : else if (m_outputBandCountProvenance != ValueProvenance::FROM_VRTRASTERBAND)
735 : {
736 8 : const GDALDataType eOutputBandType = GetOutputBandType(eInDT);
737 :
738 8 : bool bClearAndSetBands = true;
739 8 : if (nBands == nOutputBandCount)
740 : {
741 1 : bClearAndSetBands = false;
742 3 : for (int i = 0; i < nBands; ++i)
743 : {
744 2 : bClearAndSetBands =
745 2 : bClearAndSetBands ||
746 4 : !dynamic_cast<VRTProcessedRasterBand *>(papoBands[i]) ||
747 2 : papoBands[i]->GetRasterDataType() != eOutputBandType;
748 : }
749 : }
750 8 : if (bClearAndSetBands)
751 : {
752 7 : ClearBands();
753 32 : for (int i = 0; i < nOutputBandCount; ++i)
754 : {
755 25 : SetBand(i + 1, std::make_unique<VRTProcessedRasterBand>(
756 50 : this, i + 1, eOutputBandType));
757 : }
758 : }
759 : }
760 :
761 56 : if (nBands > 1)
762 41 : SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
763 :
764 56 : m_oXMLTree.reset(CPLCloneXMLTree(psTree));
765 :
766 56 : return CE_None;
767 : }
768 :
769 : /************************************************************************/
770 : /* ParseStep() */
771 : /************************************************************************/
772 :
773 : /** Parse the current Step node and create a corresponding entry in m_aoSteps.
774 : *
775 : * @param psStep Step node
776 : * @param bIsFinalStep Whether this is the final step.
777 : * @param[in,out] eCurrentDT Input data type for this step.
778 : * Updated to output data type at end of method.
779 : * @param[in,out] nCurrentBandCount Input band count for this step.
780 : * Updated to output band cout at end of
781 : * method.
782 : * @param adfInNoData Input nodata values
783 : * @param[in,out] adfOutNoData Output nodata values, to be filled by this
784 : * method. When bIsFinalStep, this is also an
785 : * input parameter.
786 : * @return true on success.
787 : */
788 89 : bool VRTProcessedDataset::ParseStep(const CPLXMLNode *psStep, bool bIsFinalStep,
789 : GDALDataType &eCurrentDT,
790 : int &nCurrentBandCount,
791 : std::vector<double> &adfInNoData,
792 : std::vector<double> &adfOutNoData)
793 : {
794 89 : const char *pszStepName = CPLGetXMLValue(
795 89 : psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
796 89 : const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
797 89 : if (!pszAlgorithm)
798 : {
799 0 : CPLError(CE_Failure, CPLE_AppDefined,
800 : "Step '%s' lacks a Algorithm element", pszStepName);
801 0 : return false;
802 : }
803 :
804 89 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
805 89 : const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
806 89 : if (oIterFunc == oMapFunctions.end())
807 : {
808 0 : CPLError(CE_Failure, CPLE_AppDefined,
809 : "Step '%s' uses unregistered algorithm '%s'", pszStepName,
810 : pszAlgorithm);
811 0 : return false;
812 : }
813 :
814 89 : const auto &oFunc = oIterFunc->second;
815 :
816 89 : if (!oFunc.aeSupportedInputDT.empty())
817 : {
818 0 : if (std::find(oFunc.aeSupportedInputDT.begin(),
819 : oFunc.aeSupportedInputDT.end(),
820 0 : eCurrentDT) == oFunc.aeSupportedInputDT.end())
821 : {
822 0 : CPLError(CE_Failure, CPLE_AppDefined,
823 : "Step '%s' (using algorithm '%s') does not "
824 : "support input data type = '%s'",
825 : pszStepName, pszAlgorithm,
826 : GDALGetDataTypeName(eCurrentDT));
827 0 : return false;
828 : }
829 : }
830 :
831 89 : if (!oFunc.anSupportedInputBandCount.empty())
832 : {
833 0 : if (std::find(oFunc.anSupportedInputBandCount.begin(),
834 : oFunc.anSupportedInputBandCount.end(),
835 0 : nCurrentBandCount) ==
836 0 : oFunc.anSupportedInputBandCount.end())
837 : {
838 0 : CPLError(CE_Failure, CPLE_AppDefined,
839 : "Step '%s' (using algorithm '%s') does not "
840 : "support input band count = %d",
841 : pszStepName, pszAlgorithm, nCurrentBandCount);
842 0 : return false;
843 : }
844 : }
845 :
846 178 : Step oStep;
847 89 : oStep.osAlgorithm = pszAlgorithm;
848 178 : oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
849 89 : ? oFunc.eRequestedInputDT
850 : : eCurrentDT;
851 89 : oStep.nInBands = nCurrentBandCount;
852 :
853 : // Unless modified by pfnInit...
854 89 : oStep.eOutDT = oStep.eInDT;
855 :
856 89 : oStep.adfInNoData = adfInNoData;
857 89 : oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
858 :
859 : // Deal with constant arguments
860 89 : for (const auto &nameValuePair : oFunc.oMapConstantArguments)
861 : {
862 : oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
863 0 : nameValuePair.second.c_str());
864 : }
865 :
866 : // Deal with built-in arguments
867 89 : if (oFunc.oSetBuiltinArguments.find("nodata") !=
868 178 : oFunc.oSetBuiltinArguments.end())
869 : {
870 0 : int bHasVal = false;
871 0 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
872 0 : const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
873 0 : if (bHasVal)
874 : {
875 : oStep.aosArguments.AddNameValue("nodata",
876 0 : CPLSPrintf("%.17g", dfVal));
877 : }
878 : }
879 :
880 89 : if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
881 178 : oFunc.oSetBuiltinArguments.end())
882 : {
883 0 : for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
884 : {
885 0 : int bHasVal = false;
886 0 : const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
887 : oStep.aosArguments.AddNameValue(
888 : CPLSPrintf("offset_%d", i),
889 0 : CPLSPrintf("%.17g", bHasVal ? dfVal : 0.0));
890 : }
891 : }
892 :
893 89 : if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
894 178 : oFunc.oSetBuiltinArguments.end())
895 : {
896 0 : for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
897 : {
898 0 : int bHasVal = false;
899 0 : const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
900 : oStep.aosArguments.AddNameValue(
901 : CPLSPrintf("scale_%d", i),
902 0 : CPLSPrintf("%.17g", bHasVal ? dfVal : 1.0));
903 : }
904 : }
905 :
906 : // Parse arguments specified in VRT
907 178 : std::set<std::string> oFoundArguments;
908 :
909 469 : for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
910 380 : psStepChild = psStepChild->psNext)
911 : {
912 380 : if (psStepChild->eType == CXT_Element &&
913 356 : strcmp(psStepChild->pszValue, "Argument") == 0)
914 : {
915 : const char *pszParamName =
916 267 : CPLGetXMLValue(psStepChild, "name", nullptr);
917 267 : if (!pszParamName)
918 : {
919 0 : CPLError(CE_Failure, CPLE_AppDefined,
920 : "Step '%s' has a Argument without a name attribute",
921 : pszStepName);
922 0 : return false;
923 : }
924 267 : const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
925 : auto oOtherArgIter =
926 267 : oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
927 534 : if (!oFunc.oOtherArguments.empty() &&
928 534 : oOtherArgIter == oFunc.oOtherArguments.end())
929 : {
930 : // If we got a parameter name like 'coefficients_1',
931 : // try to fetch the generic 'coefficients_{band}'
932 296 : std::string osParamName(pszParamName);
933 148 : const auto nPos = osParamName.rfind('_');
934 148 : if (nPos != std::string::npos)
935 : {
936 148 : osParamName.resize(nPos + 1);
937 148 : osParamName += "{band}";
938 : oOtherArgIter = oFunc.oOtherArguments.find(
939 148 : CPLString(osParamName).tolower());
940 : }
941 : }
942 267 : if (oOtherArgIter != oFunc.oOtherArguments.end())
943 : {
944 267 : oFoundArguments.insert(oOtherArgIter->first);
945 :
946 267 : const std::string &osType = oOtherArgIter->second.osType;
947 267 : if (osType == "boolean")
948 : {
949 0 : if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
950 : {
951 0 : CPLError(CE_Failure, CPLE_NotSupported,
952 : "Step '%s' has a Argument '%s' whose "
953 : "value '%s' is not a boolean",
954 : pszStepName, pszParamName, pszValue);
955 0 : return false;
956 : }
957 : }
958 267 : else if (osType == "integer")
959 : {
960 44 : if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
961 : {
962 0 : CPLError(CE_Failure, CPLE_NotSupported,
963 : "Step '%s' has a Argument '%s' whose "
964 : "value '%s' is not a integer",
965 : pszStepName, pszParamName, pszValue);
966 0 : return false;
967 : }
968 : }
969 223 : else if (osType == "double")
970 : {
971 49 : const auto eType = CPLGetValueType(pszValue);
972 49 : if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
973 : {
974 0 : CPLError(CE_Failure, CPLE_NotSupported,
975 : "Step '%s' has a Argument '%s' whose "
976 : "value '%s' is not a double",
977 : pszStepName, pszParamName, pszValue);
978 0 : return false;
979 : }
980 : }
981 174 : else if (osType == "double_list")
982 : {
983 : const CPLStringList aosTokens(
984 88 : CSLTokenizeString2(pszValue, ",", 0));
985 405 : for (int i = 0; i < aosTokens.size(); ++i)
986 : {
987 317 : const auto eType = CPLGetValueType(aosTokens[i]);
988 317 : if (eType != CPL_VALUE_INTEGER &&
989 : eType != CPL_VALUE_REAL)
990 : {
991 0 : CPLError(CE_Failure, CPLE_NotSupported,
992 : "Step '%s' has a Argument '%s' "
993 : "whose value '%s' is not a "
994 : "comma-separated list of doubles",
995 : pszStepName, pszParamName, pszValue);
996 0 : return false;
997 : }
998 : }
999 : }
1000 86 : else if (osType != "string")
1001 : {
1002 0 : CPLDebug("VRT", "Unhandled argument type '%s'",
1003 : osType.c_str());
1004 0 : CPLAssert(0);
1005 : }
1006 : }
1007 0 : else if (oFunc.bMetadataSpecified &&
1008 0 : oFunc.oSetBuiltinArguments.find(
1009 0 : CPLString(pszParamName).tolower()) ==
1010 0 : oFunc.oSetBuiltinArguments.end() &&
1011 0 : oFunc.oMapConstantArguments.find(
1012 0 : CPLString(pszParamName).tolower()) ==
1013 0 : oFunc.oMapConstantArguments.end())
1014 : {
1015 0 : CPLError(CE_Warning, CPLE_NotSupported,
1016 : "Step '%s' has a Argument '%s' which is not "
1017 : "supported",
1018 : pszStepName, pszParamName);
1019 : }
1020 :
1021 267 : oStep.aosArguments.AddNameValue(pszParamName, pszValue);
1022 : }
1023 : }
1024 :
1025 : // Check that required arguments have been specified
1026 667 : for (const auto &oIter : oFunc.oOtherArguments)
1027 : {
1028 741 : if (oIter.second.bRequired &&
1029 741 : oFoundArguments.find(oIter.first) == oFoundArguments.end())
1030 : {
1031 3 : CPLError(CE_Failure, CPLE_AppDefined,
1032 : "Step '%s' lacks required Argument '%s'", pszStepName,
1033 : oIter.first.c_str());
1034 3 : return false;
1035 : }
1036 : }
1037 :
1038 86 : if (oFunc.pfnInit)
1039 : {
1040 86 : double *padfOutNoData = nullptr;
1041 86 : if (bIsFinalStep && !adfOutNoData.empty())
1042 : {
1043 76 : oStep.nOutBands = static_cast<int>(adfOutNoData.size());
1044 76 : padfOutNoData = static_cast<double *>(
1045 76 : CPLMalloc(adfOutNoData.size() * sizeof(double)));
1046 76 : memcpy(padfOutNoData, adfOutNoData.data(),
1047 76 : adfOutNoData.size() * sizeof(double));
1048 : }
1049 : else
1050 : {
1051 10 : oStep.nOutBands = 0;
1052 : }
1053 :
1054 172 : if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
1055 86 : oStep.aosArguments.List(), oStep.nInBands,
1056 : oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
1057 : &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
1058 86 : &(oStep.pWorkingData)) != CE_None)
1059 : {
1060 28 : CPLError(CE_Failure, CPLE_AppDefined,
1061 : "Step '%s' (using algorithm '%s') init() function "
1062 : "failed",
1063 : pszStepName, pszAlgorithm);
1064 28 : CPLFree(padfOutNoData);
1065 28 : return false;
1066 : }
1067 :
1068 : // Input nodata values may have been modified by pfnInit()
1069 58 : oStep.adfInNoData = adfInNoData;
1070 :
1071 58 : if (padfOutNoData)
1072 : {
1073 108 : adfOutNoData = std::vector<double>(padfOutNoData,
1074 54 : padfOutNoData + oStep.nOutBands);
1075 : }
1076 : else
1077 : {
1078 12 : adfOutNoData = std::vector<double>(
1079 8 : oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
1080 : }
1081 58 : CPLFree(padfOutNoData);
1082 :
1083 58 : oStep.adfOutNoData = adfOutNoData;
1084 : }
1085 : else
1086 : {
1087 0 : oStep.nOutBands = oStep.nInBands;
1088 0 : adfOutNoData = oStep.adfOutNoData;
1089 : }
1090 :
1091 58 : eCurrentDT = oStep.eOutDT;
1092 58 : nCurrentBandCount = oStep.nOutBands;
1093 :
1094 58 : m_aoSteps.emplace_back(std::move(oStep));
1095 :
1096 58 : return true;
1097 : }
1098 :
1099 : /************************************************************************/
1100 : /* SerializeToXML() */
1101 : /************************************************************************/
1102 :
1103 1 : CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
1104 :
1105 : {
1106 1 : CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
1107 1 : if (psTree == nullptr)
1108 0 : return psTree;
1109 :
1110 : /* -------------------------------------------------------------------- */
1111 : /* Remove VRTRasterBand nodes from the original tree and find the */
1112 : /* last child. */
1113 : /* -------------------------------------------------------------------- */
1114 1 : CPLXMLNode *psLastChild = psTree->psChild;
1115 1 : CPLXMLNode *psPrevChild = nullptr;
1116 5 : while (psLastChild)
1117 : {
1118 5 : CPLXMLNode *psNextChild = psLastChild->psNext;
1119 5 : if (psLastChild->eType == CXT_Element &&
1120 3 : strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
1121 : {
1122 1 : if (psPrevChild)
1123 1 : psPrevChild->psNext = psNextChild;
1124 : else
1125 0 : psTree->psChild = psNextChild;
1126 1 : psLastChild->psNext = nullptr;
1127 1 : CPLDestroyXMLNode(psLastChild);
1128 1 : psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
1129 : }
1130 4 : else if (!psNextChild)
1131 : {
1132 1 : break;
1133 : }
1134 : else
1135 : {
1136 3 : psPrevChild = psLastChild;
1137 3 : psLastChild = psNextChild;
1138 : }
1139 : }
1140 1 : CPLAssert(psLastChild); // we have at least Input
1141 :
1142 : /* -------------------------------------------------------------------- */
1143 : /* Serialize bands. */
1144 : /* -------------------------------------------------------------------- */
1145 1 : bool bHasWarnedAboutRAMUsage = false;
1146 1 : size_t nAccRAMUsage = 0;
1147 2 : for (int iBand = 0; iBand < nBands; iBand++)
1148 : {
1149 : CPLXMLNode *psBandTree =
1150 1 : static_cast<VRTRasterBand *>(papoBands[iBand])
1151 2 : ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
1152 1 : nAccRAMUsage);
1153 :
1154 1 : if (psBandTree != nullptr)
1155 : {
1156 1 : psLastChild->psNext = psBandTree;
1157 1 : psLastChild = psBandTree;
1158 : }
1159 : }
1160 :
1161 1 : return psTree;
1162 : }
1163 :
1164 : /************************************************************************/
1165 : /* SerializeToXML() */
1166 : /************************************************************************/
1167 :
1168 : CPLXMLNode *
1169 1 : VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
1170 : bool &bHasWarnedAboutRAMUsage,
1171 : size_t &nAccRAMUsage)
1172 :
1173 : {
1174 1 : CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1175 : pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1176 :
1177 : /* -------------------------------------------------------------------- */
1178 : /* Set subclass. */
1179 : /* -------------------------------------------------------------------- */
1180 1 : CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1181 : CXT_Text, "VRTProcessedRasterBand");
1182 :
1183 1 : return psTree;
1184 : }
1185 :
1186 : /************************************************************************/
1187 : /* GetBlockSize() */
1188 : /************************************************************************/
1189 :
1190 : /** Return block size */
1191 140 : void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
1192 : int *pnBlockYSize) const
1193 :
1194 : {
1195 140 : *pnBlockXSize = m_nBlockXSize;
1196 140 : *pnBlockYSize = m_nBlockYSize;
1197 140 : }
1198 :
1199 : /************************************************************************/
1200 : /* ProcessRegion() */
1201 : /************************************************************************/
1202 :
1203 : /** Compute pixel values for the specified region.
1204 : *
1205 : * The output is stored in m_abyInput in a pixel-interleaved way.
1206 : */
1207 70 : bool VRTProcessedDataset::ProcessRegion(int nXOff, int nYOff, int nBufXSize,
1208 : int nBufYSize,
1209 : GDALProgressFunc pfnProgress,
1210 : void *pProgressData)
1211 : {
1212 :
1213 70 : CPLAssert(!m_aoSteps.empty());
1214 :
1215 70 : const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
1216 :
1217 70 : const int nFirstBandCount = m_aoSteps.front().nInBands;
1218 70 : CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
1219 70 : const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
1220 70 : const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
1221 70 : auto &abyInput = m_abyInput;
1222 70 : auto &abyOutput = m_abyOutput;
1223 :
1224 : const char *pszInterleave =
1225 70 : m_poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
1226 70 : if (nFirstBandCount > 1 && (!pszInterleave || EQUAL(pszInterleave, "BAND")))
1227 : {
1228 : // If there are several bands and the source dataset organization
1229 : // is apparently band interleaved, then first acquire data in
1230 : // a BSQ organization in the abyInput array use in the native
1231 : // data type.
1232 : // And then transpose it and convert it to the expected data type
1233 : // of the first step.
1234 1 : const auto eSrcDT = m_poSrcDS->GetRasterBand(1)->GetRasterDataType();
1235 : try
1236 : {
1237 2 : abyInput.resize(nPixelCount * nFirstBandCount *
1238 1 : GDALGetDataTypeSizeBytes(eSrcDT));
1239 : }
1240 0 : catch (const std::bad_alloc &)
1241 : {
1242 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1243 : "Out of memory allocating working buffer");
1244 0 : return false;
1245 : }
1246 :
1247 : try
1248 : {
1249 1 : abyOutput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1250 : }
1251 0 : catch (const std::bad_alloc &)
1252 : {
1253 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1254 : "Out of memory allocating working buffer");
1255 0 : return false;
1256 : }
1257 :
1258 : GDALRasterIOExtraArg sArg;
1259 1 : INIT_RASTERIO_EXTRA_ARG(sArg);
1260 1 : sArg.pfnProgress = GDALScaledProgress;
1261 1 : sArg.pProgressData =
1262 1 : GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1263 1 : if (sArg.pProgressData == nullptr)
1264 1 : sArg.pfnProgress = nullptr;
1265 :
1266 1 : CPLDebugOnly("VRT", "ProcessRegion(): start RasterIO()");
1267 : const bool bOK =
1268 2 : m_poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nBufXSize, nBufYSize,
1269 1 : abyInput.data(), nBufXSize, nBufYSize, eSrcDT,
1270 : nFirstBandCount, nullptr, 0, 0, 0,
1271 1 : &sArg) == CE_None;
1272 1 : CPLDebugOnly("VRT", "ProcessRegion(): end RasterIO()");
1273 1 : GDALDestroyScaledProgress(sArg.pProgressData);
1274 1 : if (!bOK)
1275 0 : return false;
1276 :
1277 1 : CPLDebugOnly("VRT", "ProcessRegion(): start GDALTranspose2D()");
1278 1 : GDALTranspose2D(abyInput.data(), eSrcDT, abyOutput.data(), eFirstDT,
1279 1 : static_cast<size_t>(nBufXSize) * nBufYSize,
1280 : nFirstBandCount);
1281 1 : CPLDebugOnly("VRT", "ProcessRegion(): end GDALTranspose2D()");
1282 :
1283 : // Swap arrays
1284 1 : std::swap(abyInput, abyOutput);
1285 : }
1286 : else
1287 : {
1288 : try
1289 : {
1290 69 : abyInput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
1291 : }
1292 0 : catch (const std::bad_alloc &)
1293 : {
1294 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1295 : "Out of memory allocating working buffer");
1296 0 : return false;
1297 : }
1298 :
1299 : GDALRasterIOExtraArg sArg;
1300 69 : INIT_RASTERIO_EXTRA_ARG(sArg);
1301 69 : sArg.pfnProgress = GDALScaledProgress;
1302 69 : sArg.pProgressData =
1303 69 : GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
1304 69 : if (sArg.pProgressData == nullptr)
1305 69 : sArg.pfnProgress = nullptr;
1306 :
1307 : const bool bOK =
1308 138 : m_poSrcDS->RasterIO(
1309 69 : GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
1310 : nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
1311 69 : static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
1312 69 : static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount *
1313 69 : nBufXSize,
1314 69 : nFirstDTSize, &sArg) == CE_None;
1315 :
1316 69 : GDALDestroyScaledProgress(sArg.pProgressData);
1317 69 : if (!bOK)
1318 3 : return false;
1319 : }
1320 :
1321 67 : const double dfSrcXOff = nXOff;
1322 67 : const double dfSrcYOff = nYOff;
1323 67 : const double dfSrcXSize = nBufXSize;
1324 67 : const double dfSrcYSize = nBufYSize;
1325 :
1326 67 : GDALGeoTransform srcGT;
1327 67 : if (m_poSrcDS->GetGeoTransform(srcGT) != CE_None)
1328 : {
1329 38 : srcGT = GDALGeoTransform();
1330 : }
1331 :
1332 67 : GDALDataType eLastDT = eFirstDT;
1333 67 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
1334 :
1335 67 : int iStep = 0;
1336 132 : for (const auto &oStep : m_aoSteps)
1337 : {
1338 69 : const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
1339 69 : CPLAssert(oIterFunc != oMapFunctions.end());
1340 :
1341 : // Data type adaptation
1342 69 : if (eLastDT != oStep.eInDT)
1343 : {
1344 : try
1345 : {
1346 0 : abyOutput.resize(nPixelCount * oStep.nInBands *
1347 0 : GDALGetDataTypeSizeBytes(oStep.eInDT));
1348 : }
1349 0 : catch (const std::bad_alloc &)
1350 : {
1351 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1352 : "Out of memory allocating working buffer");
1353 0 : return false;
1354 : }
1355 :
1356 0 : GDALCopyWords64(abyInput.data(), eLastDT,
1357 0 : GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
1358 0 : oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
1359 0 : nPixelCount * oStep.nInBands);
1360 :
1361 0 : std::swap(abyInput, abyOutput);
1362 : }
1363 :
1364 : try
1365 : {
1366 138 : abyOutput.resize(nPixelCount * oStep.nOutBands *
1367 69 : GDALGetDataTypeSizeBytes(oStep.eOutDT));
1368 : }
1369 0 : catch (const std::bad_alloc &)
1370 : {
1371 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1372 : "Out of memory allocating working buffer");
1373 0 : return false;
1374 : }
1375 :
1376 69 : const auto &oFunc = oIterFunc->second;
1377 345 : if (oFunc.pfnProcess(
1378 69 : oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
1379 : oStep.aosArguments.List(), nBufXSize, nBufYSize,
1380 69 : abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
1381 69 : oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
1382 69 : oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
1383 69 : dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, srcGT.data(),
1384 : m_osVRTPath.c_str(),
1385 69 : /*papszExtra=*/nullptr) != CE_None)
1386 : {
1387 4 : return false;
1388 : }
1389 :
1390 65 : std::swap(abyInput, abyOutput);
1391 65 : eLastDT = oStep.eOutDT;
1392 :
1393 65 : ++iStep;
1394 65 : if (pfnProgress &&
1395 0 : !pfnProgress(0.5 + 0.5 * iStep / static_cast<int>(m_aoSteps.size()),
1396 : "", pProgressData))
1397 0 : return false;
1398 : }
1399 :
1400 63 : return true;
1401 : }
1402 :
1403 : /************************************************************************/
1404 : /* VRTProcessedRasterBand() */
1405 : /************************************************************************/
1406 :
1407 : /** Constructor */
1408 140 : VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
1409 : int nBandIn,
1410 140 : GDALDataType eDataTypeIn)
1411 : {
1412 140 : Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
1413 :
1414 140 : poDS = poDSIn;
1415 140 : nBand = nBandIn;
1416 140 : eAccess = GA_Update;
1417 140 : eDataType = eDataTypeIn;
1418 :
1419 140 : poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
1420 140 : }
1421 :
1422 : /************************************************************************/
1423 : /* GetOverviewCount() */
1424 : /************************************************************************/
1425 :
1426 6 : int VRTProcessedRasterBand::GetOverviewCount()
1427 : {
1428 6 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1429 6 : return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
1430 : }
1431 :
1432 : /************************************************************************/
1433 : /* GetOverview() */
1434 : /************************************************************************/
1435 :
1436 11 : GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
1437 : {
1438 11 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1439 22 : if (iOvr < 0 ||
1440 11 : iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
1441 0 : return nullptr;
1442 11 : return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
1443 : }
1444 :
1445 : /************************************************************************/
1446 : /* IReadBlock() */
1447 : /************************************************************************/
1448 :
1449 45 : CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1450 : void *pImage)
1451 :
1452 : {
1453 45 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1454 :
1455 45 : int nBufXSize = 0;
1456 45 : int nBufYSize = 0;
1457 45 : GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
1458 :
1459 45 : const int nXPixelOff = nBlockXOff * nBlockXSize;
1460 45 : const int nYPixelOff = nBlockYOff * nBlockYSize;
1461 45 : if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize,
1462 : nullptr, nullptr))
1463 : {
1464 3 : return CE_Failure;
1465 : }
1466 :
1467 42 : const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
1468 42 : CPLAssert(nOutBands == poVRTDS->GetRasterCount());
1469 42 : const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
1470 42 : const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1471 42 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1472 :
1473 : // Dispatch final output buffer to cached blocks of output bands
1474 120 : for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
1475 : {
1476 78 : GDALRasterBlock *poBlock = nullptr;
1477 : GByte *pDst;
1478 78 : if (iDstBand + 1 == nBand)
1479 : {
1480 42 : pDst = static_cast<GByte *>(pImage);
1481 : }
1482 : else
1483 : {
1484 36 : auto poOtherBand = poVRTDS->papoBands[iDstBand];
1485 36 : poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1486 36 : if (poBlock)
1487 : {
1488 0 : poBlock->DropLock();
1489 0 : continue;
1490 : }
1491 72 : poBlock = poOtherBand->GetLockedBlockRef(
1492 36 : nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
1493 36 : if (!poBlock)
1494 0 : continue;
1495 36 : pDst = static_cast<GByte *>(poBlock->GetDataRef());
1496 : }
1497 853 : for (int iY = 0; iY < nBufYSize; ++iY)
1498 : {
1499 1550 : GDALCopyWords64(poVRTDS->m_abyInput.data() +
1500 775 : (iDstBand + static_cast<size_t>(iY) *
1501 775 : nBufXSize * nOutBands) *
1502 775 : nLastDTSize,
1503 : eLastDT, nLastDTSize * nOutBands,
1504 775 : pDst +
1505 775 : static_cast<size_t>(iY) * nBlockXSize * nDTSize,
1506 : eDataType, nDTSize, nBufXSize);
1507 : }
1508 78 : if (poBlock)
1509 36 : poBlock->DropLock();
1510 : }
1511 :
1512 42 : return CE_None;
1513 : }
1514 :
1515 : /************************************************************************/
1516 : /* VRTProcessedDataset::IRasterIO() */
1517 : /************************************************************************/
1518 :
1519 56 : CPLErr VRTProcessedDataset::IRasterIO(
1520 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1521 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1522 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1523 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1524 : {
1525 : // Try to pass the request to the most appropriate overview dataset.
1526 56 : if (nBufXSize < nXSize && nBufYSize < nYSize)
1527 : {
1528 2 : int bTried = FALSE;
1529 2 : const CPLErr eErr = TryOverviewRasterIO(
1530 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1531 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1532 : nBandSpace, psExtraArg, &bTried);
1533 2 : if (bTried)
1534 2 : return eErr;
1535 : }
1536 :
1537 : // Optimize reading of all bands at nominal resolution for BIP-like or
1538 : // BSQ-like buffer spacing.
1539 54 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
1540 53 : nBandCount == nBands)
1541 : {
1542 52 : const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1543 61 : const bool bIsBIPLike = nBandSpace == nBufTypeSize &&
1544 9 : nPixelSpace == nBandSpace * nBands &&
1545 66 : nLineSpace >= nPixelSpace * nBufXSize &&
1546 5 : IsAllBands(nBandCount, panBandMap);
1547 99 : const bool bIsBSQLike = nPixelSpace == nBufTypeSize &&
1548 47 : nLineSpace >= nPixelSpace * nBufXSize &&
1549 146 : nBandSpace >= nLineSpace * nBufYSize &&
1550 47 : IsAllBands(nBandCount, panBandMap);
1551 52 : if (bIsBIPLike || bIsBSQLike)
1552 : {
1553 50 : GByte *pabyData = static_cast<GByte *>(pData);
1554 : // If acquiring the region of interest in a single time is going
1555 : // to consume too much RAM, split in halves.
1556 50 : if (m_nAllowedRAMUsage > 0 &&
1557 50 : static_cast<GIntBig>(nBufXSize) * nBufYSize >
1558 50 : m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
1559 : {
1560 25 : if ((nBufXSize == nRasterXSize || nBufYSize >= nBufXSize) &&
1561 : nBufYSize >= 2)
1562 : {
1563 : GDALRasterIOExtraArg sArg;
1564 12 : INIT_RASTERIO_EXTRA_ARG(sArg);
1565 12 : const int nHalfHeight = nBufYSize / 2;
1566 :
1567 12 : sArg.pfnProgress = GDALScaledProgress;
1568 12 : sArg.pProgressData = GDALCreateScaledProgress(
1569 : 0, 0.5, psExtraArg->pfnProgress,
1570 : psExtraArg->pProgressData);
1571 12 : if (sArg.pProgressData == nullptr)
1572 12 : sArg.pfnProgress = nullptr;
1573 : bool bOK =
1574 12 : IRasterIO(eRWFlag, nXOff, nYOff, nBufXSize, nHalfHeight,
1575 : pabyData, nBufXSize, nHalfHeight, eBufType,
1576 : nBandCount, panBandMap, nPixelSpace,
1577 12 : nLineSpace, nBandSpace, &sArg) == CE_None;
1578 12 : GDALDestroyScaledProgress(sArg.pProgressData);
1579 :
1580 12 : if (bOK)
1581 : {
1582 2 : sArg.pfnProgress = GDALScaledProgress;
1583 2 : sArg.pProgressData = GDALCreateScaledProgress(
1584 : 0.5, 1, psExtraArg->pfnProgress,
1585 : psExtraArg->pProgressData);
1586 2 : if (sArg.pProgressData == nullptr)
1587 2 : sArg.pfnProgress = nullptr;
1588 4 : bOK = IRasterIO(eRWFlag, nXOff, nYOff + nHalfHeight,
1589 : nBufXSize, nBufYSize - nHalfHeight,
1590 2 : pabyData + nHalfHeight * nLineSpace,
1591 : nBufXSize, nBufYSize - nHalfHeight,
1592 : eBufType, nBandCount, panBandMap,
1593 : nPixelSpace, nLineSpace, nBandSpace,
1594 : &sArg) == CE_None;
1595 2 : GDALDestroyScaledProgress(sArg.pProgressData);
1596 : }
1597 12 : return bOK ? CE_None : CE_Failure;
1598 : }
1599 13 : else if (nBufXSize >= 2)
1600 : {
1601 : GDALRasterIOExtraArg sArg;
1602 13 : INIT_RASTERIO_EXTRA_ARG(sArg);
1603 13 : const int nHalfWidth = nBufXSize / 2;
1604 :
1605 13 : sArg.pfnProgress = GDALScaledProgress;
1606 13 : sArg.pProgressData = GDALCreateScaledProgress(
1607 : 0, 0.5, psExtraArg->pfnProgress,
1608 : psExtraArg->pProgressData);
1609 13 : if (sArg.pProgressData == nullptr)
1610 13 : sArg.pfnProgress = nullptr;
1611 : bool bOK =
1612 13 : IRasterIO(eRWFlag, nXOff, nYOff, nHalfWidth, nBufYSize,
1613 : pabyData, nHalfWidth, nBufYSize, eBufType,
1614 : nBandCount, panBandMap, nPixelSpace,
1615 13 : nLineSpace, nBandSpace, &sArg) == CE_None;
1616 13 : GDALDestroyScaledProgress(sArg.pProgressData);
1617 :
1618 13 : if (bOK)
1619 : {
1620 3 : sArg.pfnProgress = GDALScaledProgress;
1621 3 : sArg.pProgressData = GDALCreateScaledProgress(
1622 : 0.5, 1, psExtraArg->pfnProgress,
1623 : psExtraArg->pProgressData);
1624 3 : if (sArg.pProgressData == nullptr)
1625 3 : sArg.pfnProgress = nullptr;
1626 6 : bOK = IRasterIO(eRWFlag, nXOff + nHalfWidth, nYOff,
1627 : nBufXSize - nHalfWidth, nBufYSize,
1628 3 : pabyData + nHalfWidth * nPixelSpace,
1629 : nBufXSize - nHalfWidth, nBufYSize,
1630 : eBufType, nBandCount, panBandMap,
1631 : nPixelSpace, nLineSpace, nBandSpace,
1632 : &sArg) == CE_None;
1633 3 : GDALDestroyScaledProgress(sArg.pProgressData);
1634 : }
1635 13 : return bOK ? CE_None : CE_Failure;
1636 : }
1637 : }
1638 :
1639 25 : if (!ProcessRegion(nXOff, nYOff, nBufXSize, nBufYSize,
1640 : psExtraArg->pfnProgress,
1641 : psExtraArg->pProgressData))
1642 : {
1643 4 : return CE_Failure;
1644 : }
1645 21 : const auto eLastDT = m_aoSteps.back().eOutDT;
1646 21 : const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1647 21 : if (bIsBIPLike)
1648 : {
1649 10 : for (int iY = 0; iY < nBufYSize; ++iY)
1650 : {
1651 21 : GDALCopyWords64(
1652 14 : m_abyInput.data() + static_cast<size_t>(iY) * nBands *
1653 7 : nBufXSize * nLastDTSize,
1654 7 : eLastDT, nLastDTSize, pabyData + iY * nLineSpace,
1655 : eBufType, GDALGetDataTypeSizeBytes(eBufType),
1656 7 : static_cast<size_t>(nBufXSize) * nBands);
1657 : }
1658 : }
1659 : else
1660 : {
1661 18 : CPLAssert(bIsBSQLike);
1662 92 : for (int iBand = 0; iBand < nBands; ++iBand)
1663 : {
1664 168 : for (int iY = 0; iY < nBufYSize; ++iY)
1665 : {
1666 188 : GDALCopyWords64(
1667 188 : m_abyInput.data() +
1668 94 : (static_cast<size_t>(iY) * nBands * nBufXSize +
1669 94 : iBand) *
1670 94 : nLastDTSize,
1671 94 : eLastDT, nLastDTSize * nBands,
1672 94 : pabyData + iBand * nBandSpace + iY * nLineSpace,
1673 : eBufType, nBufTypeSize, nBufXSize);
1674 : }
1675 : }
1676 : }
1677 21 : return CE_None;
1678 : }
1679 : }
1680 :
1681 4 : return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1682 : nBufXSize, nBufYSize, eBufType, nBandCount,
1683 : panBandMap, nPixelSpace, nLineSpace,
1684 4 : nBandSpace, psExtraArg);
1685 : }
1686 :
1687 : /*! @endcond */
1688 :
1689 : /************************************************************************/
1690 : /* GDALVRTRegisterProcessedDatasetFunc() */
1691 : /************************************************************************/
1692 :
1693 : /** Register a function to be used by VRTProcessedDataset.
1694 :
1695 : An example of content for pszXMLMetadata is:
1696 : \verbatim
1697 : <ProcessedDatasetFunctionArgumentsList>
1698 : <Argument name='src_nodata' type='double' description='Override input nodata value'/>
1699 : <Argument name='dst_nodata' type='double' description='Override output nodata value'/>
1700 : <Argument name='replacement_nodata' description='value to substitute to a valid computed value that would be nodata' type='double'/>
1701 : <Argument name='dst_intended_datatype' type='string' description='Intented datatype of output (which might be different than the working data type)'/>
1702 : <Argument name='coefficients_{band}' description='Comma-separated coefficients for combining bands. First one is constant term' type='double_list' required='true'/>
1703 : </ProcessedDatasetFunctionArgumentsList>
1704 : \endverbatim
1705 :
1706 : @param pszFuncName Function name. Must be unique and not null.
1707 : @param pUserData User data. May be nullptr. Must remain valid during the
1708 : lifetime of GDAL.
1709 : @param pszXMLMetadata XML metadata describing the function arguments. May be
1710 : nullptr if there are no arguments.
1711 : @param eRequestedInputDT If the pfnProcess callback only supports a single
1712 : data type, it should be specified in this parameter.
1713 : Otherwise set it to GDT_Unknown.
1714 : @param paeSupportedInputDT List of supported input data types. May be nullptr
1715 : if all are supported or if eRequestedInputDT is
1716 : set to a non GDT_Unknown value.
1717 : @param nSupportedInputDTSize Size of paeSupportedInputDT
1718 : @param panSupportedInputBandCount List of supported band count. May be nullptr
1719 : if any source band count is supported.
1720 : @param nSupportedInputBandCountSize Size of panSupportedInputBandCount
1721 : @param pfnInit Initialization function called when a VRTProcessedDataset
1722 : step uses the register function. This initialization function
1723 : will return the output data type, output band count and
1724 : potentially initialize a working structure, typically parsing
1725 : arguments. May be nullptr.
1726 : If not specified, it will be assumed that the input and output
1727 : data types are the same, and that the input number of bands
1728 : and output number of bands are the same.
1729 : @param pfnFree Free function that will free the working structure allocated
1730 : by pfnInit. May be nullptr.
1731 : @param pfnProcess Processing function called to compute pixel values. Must
1732 : not be nullptr.
1733 : @param papszOptions Unused currently. Must be nullptr.
1734 : @return CE_None in case of success, error otherwise.
1735 : @since 3.9
1736 : */
1737 7265 : CPLErr GDALVRTRegisterProcessedDatasetFunc(
1738 : const char *pszFuncName, void *pUserData, const char *pszXMLMetadata,
1739 : GDALDataType eRequestedInputDT, const GDALDataType *paeSupportedInputDT,
1740 : size_t nSupportedInputDTSize, const int *panSupportedInputBandCount,
1741 : size_t nSupportedInputBandCountSize,
1742 : GDALVRTProcessedDatasetFuncInit pfnInit,
1743 : GDALVRTProcessedDatasetFuncFree pfnFree,
1744 : GDALVRTProcessedDatasetFuncProcess pfnProcess,
1745 : CPL_UNUSED CSLConstList papszOptions)
1746 : {
1747 7265 : if (pszFuncName == nullptr || pszFuncName[0] == '\0')
1748 : {
1749 0 : CPLError(CE_Failure, CPLE_AppDefined,
1750 : "pszFuncName should be non-empty");
1751 0 : return CE_Failure;
1752 : }
1753 :
1754 7265 : auto &oMap = GetGlobalMapProcessedDatasetFunc();
1755 7265 : if (oMap.find(pszFuncName) != oMap.end())
1756 : {
1757 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
1758 : pszFuncName);
1759 0 : return CE_Failure;
1760 : }
1761 :
1762 7265 : if (!pfnProcess)
1763 : {
1764 0 : CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
1765 0 : return CE_Failure;
1766 : }
1767 :
1768 14530 : VRTProcessedDatasetFunc oFunc;
1769 7265 : oFunc.osFuncName = pszFuncName;
1770 7265 : oFunc.pUserData = pUserData;
1771 7265 : if (pszXMLMetadata)
1772 : {
1773 7265 : oFunc.bMetadataSpecified = true;
1774 7265 : auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
1775 7265 : if (!psTree)
1776 : {
1777 0 : CPLError(CE_Failure, CPLE_AppDefined,
1778 : "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
1779 : pszFuncName);
1780 0 : return CE_Failure;
1781 : }
1782 7265 : const CPLXMLNode *psRoot = CPLGetXMLNode(
1783 : psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
1784 7265 : if (!psRoot)
1785 : {
1786 0 : CPLError(CE_Failure, CPLE_AppDefined,
1787 : "No root ProcessedDatasetFunctionArgumentsList element in "
1788 : "pszXMLMetadata=%s for %s",
1789 : pszXMLMetadata, pszFuncName);
1790 0 : return CE_Failure;
1791 : }
1792 55214 : for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
1793 47949 : psIter = psIter->psNext)
1794 : {
1795 47949 : if (psIter->eType == CXT_Element &&
1796 47949 : strcmp(psIter->pszValue, "Argument") == 0)
1797 : {
1798 47949 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
1799 47949 : if (!pszName)
1800 : {
1801 0 : CPLError(CE_Failure, CPLE_AppDefined,
1802 : "Missing Argument.name attribute in "
1803 : "pszXMLMetadata=%s for %s",
1804 : pszXMLMetadata, pszFuncName);
1805 0 : return CE_Failure;
1806 : }
1807 47949 : const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
1808 47949 : if (!pszType)
1809 : {
1810 0 : CPLError(CE_Failure, CPLE_AppDefined,
1811 : "Missing Argument.type attribute in "
1812 : "pszXMLMetadata=%s for %s",
1813 : pszXMLMetadata, pszFuncName);
1814 0 : return CE_Failure;
1815 : }
1816 47949 : if (strcmp(pszType, "constant") == 0)
1817 : {
1818 : const char *pszValue =
1819 0 : CPLGetXMLValue(psIter, "value", nullptr);
1820 0 : if (!pszValue)
1821 : {
1822 0 : CPLError(CE_Failure, CPLE_AppDefined,
1823 : "Missing Argument.value attribute in "
1824 : "pszXMLMetadata=%s for %s",
1825 : pszXMLMetadata, pszFuncName);
1826 0 : return CE_Failure;
1827 : }
1828 0 : oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
1829 0 : pszValue;
1830 : }
1831 47949 : else if (strcmp(pszType, "builtin") == 0)
1832 : {
1833 0 : if (EQUAL(pszName, "nodata") ||
1834 0 : EQUAL(pszName, "offset_{band}") ||
1835 0 : EQUAL(pszName, "scale_{band}"))
1836 : {
1837 0 : oFunc.oSetBuiltinArguments.insert(
1838 0 : CPLString(pszName).tolower());
1839 : }
1840 : else
1841 : {
1842 0 : CPLError(CE_Failure, CPLE_NotSupported,
1843 : "Unsupported builtin parameter name %s in "
1844 : "pszXMLMetadata=%s for %s. Only nodata, "
1845 : "offset_{band} and scale_{band} are supported",
1846 : pszName, pszXMLMetadata, pszFuncName);
1847 0 : return CE_Failure;
1848 : }
1849 : }
1850 47949 : else if (strcmp(pszType, "boolean") == 0 ||
1851 45043 : strcmp(pszType, "string") == 0 ||
1852 34872 : strcmp(pszType, "integer") == 0 ||
1853 26154 : strcmp(pszType, "double") == 0 ||
1854 1453 : strcmp(pszType, "double_list") == 0)
1855 : {
1856 47949 : VRTProcessedDatasetFunc::OtherArgument otherArgument;
1857 47949 : otherArgument.bRequired = CPLTestBool(
1858 : CPLGetXMLValue(psIter, "required", "false"));
1859 47949 : otherArgument.osType = pszType;
1860 95898 : oFunc.oOtherArguments[CPLString(pszName).tolower()] =
1861 143847 : std::move(otherArgument);
1862 : }
1863 : else
1864 : {
1865 0 : CPLError(CE_Failure, CPLE_NotSupported,
1866 : "Unsupported type for parameter %s in "
1867 : "pszXMLMetadata=%s for %s. Only boolean, string, "
1868 : "integer, double and double_list are supported",
1869 : pszName, pszXMLMetadata, pszFuncName);
1870 0 : return CE_Failure;
1871 : }
1872 : }
1873 : }
1874 : }
1875 7265 : oFunc.eRequestedInputDT = eRequestedInputDT;
1876 7265 : if (nSupportedInputDTSize)
1877 : {
1878 : oFunc.aeSupportedInputDT.insert(
1879 0 : oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
1880 0 : paeSupportedInputDT + nSupportedInputDTSize);
1881 : }
1882 7265 : if (nSupportedInputBandCountSize)
1883 : {
1884 : oFunc.anSupportedInputBandCount.insert(
1885 0 : oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
1886 0 : panSupportedInputBandCount + nSupportedInputBandCountSize);
1887 : }
1888 7265 : oFunc.pfnInit = pfnInit;
1889 7265 : oFunc.pfnFree = pfnFree;
1890 7265 : oFunc.pfnProcess = pfnProcess;
1891 :
1892 7265 : oMap[pszFuncName] = std::move(oFunc);
1893 :
1894 7265 : return CE_None;
1895 : }
|