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