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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_minixml.h"
30 : #include "cpl_string.h"
31 : #include "vrtdataset.h"
32 :
33 : #include <algorithm>
34 : #include <limits>
35 : #include <map>
36 : #include <vector>
37 :
38 : /************************************************************************/
39 : /* VRTProcessedDatasetFunc */
40 : /************************************************************************/
41 :
42 : //! Structure holding information for a VRTProcessedDataset function.
43 : struct VRTProcessedDatasetFunc
44 : {
45 : //! Processing function name
46 : std::string osFuncName{};
47 :
48 : //! User data to provide to pfnInit, pfnFree, pfnProcess callbacks.
49 : void *pUserData = nullptr;
50 :
51 : //! Whether XML metadata has been specified
52 : bool bMetadataSpecified = false;
53 :
54 : //! Map of (constant argument name, constant value)
55 : std::map<std::string, std::string> oMapConstantArguments{};
56 :
57 : //! Set of builtin argument names (e.g "offset", "scale", "nodata")
58 : std::set<std::string> oSetBuiltinArguments{};
59 :
60 : //! Arguments defined in the VRT
61 : struct OtherArgument
62 : {
63 : std::string osType{};
64 : bool bRequired = false;
65 : };
66 :
67 : std::map<std::string, OtherArgument> oOtherArguments{};
68 :
69 : //! Requested input data type.
70 : GDALDataType eRequestedInputDT = GDT_Unknown;
71 :
72 : //! List of supported input datatypes. Empty if no restriction.
73 : std::vector<GDALDataType> aeSupportedInputDT{};
74 :
75 : //! List of supported input band counts. Empty if no restriction.
76 : std::vector<int> anSupportedInputBandCount{};
77 :
78 : //! Optional initialization function
79 : GDALVRTProcessedDatasetFuncInit pfnInit = nullptr;
80 :
81 : //! Optional free function
82 : GDALVRTProcessedDatasetFuncFree pfnFree = nullptr;
83 :
84 : //! Required processing function
85 : GDALVRTProcessedDatasetFuncProcess pfnProcess = nullptr;
86 : };
87 :
88 : /************************************************************************/
89 : /* GetGlobalMapProcessedDatasetFunc() */
90 : /************************************************************************/
91 :
92 : /** Return the registry of VRTProcessedDatasetFunc functions */
93 : static std::map<std::string, VRTProcessedDatasetFunc> &
94 4942 : GetGlobalMapProcessedDatasetFunc()
95 : {
96 4942 : static std::map<std::string, VRTProcessedDatasetFunc> goMap;
97 4942 : return goMap;
98 : }
99 :
100 : /************************************************************************/
101 : /* Step::~Step() */
102 : /************************************************************************/
103 :
104 : /*! @cond Doxygen_Suppress */
105 :
106 : /** Step destructor */
107 60 : VRTProcessedDataset::Step::~Step()
108 : {
109 60 : deinit();
110 60 : }
111 :
112 : /************************************************************************/
113 : /* Step::deinit() */
114 : /************************************************************************/
115 :
116 : /** Free pWorkingData */
117 60 : void VRTProcessedDataset::Step::deinit()
118 : {
119 60 : if (pWorkingData)
120 : {
121 15 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
122 15 : const auto oIterFunc = oMapFunctions.find(osAlgorithm);
123 15 : if (oIterFunc != oMapFunctions.end())
124 : {
125 15 : if (oIterFunc->second.pfnFree)
126 : {
127 30 : oIterFunc->second.pfnFree(osAlgorithm.c_str(),
128 15 : oIterFunc->second.pUserData,
129 : pWorkingData);
130 : }
131 : }
132 : else
133 : {
134 0 : CPLAssert(false);
135 : }
136 15 : pWorkingData = nullptr;
137 : }
138 60 : }
139 :
140 : /************************************************************************/
141 : /* Step::Step(Step&& other) */
142 : /************************************************************************/
143 :
144 : /** Move constructor */
145 18 : VRTProcessedDataset::Step::Step(Step &&other)
146 18 : : osAlgorithm(std::move(other.osAlgorithm)),
147 36 : aosArguments(std::move(other.aosArguments)), eInDT(other.eInDT),
148 18 : eOutDT(other.eOutDT), nInBands(other.nInBands),
149 18 : nOutBands(other.nOutBands), adfInNoData(other.adfInNoData),
150 18 : adfOutNoData(other.adfOutNoData), pWorkingData(other.pWorkingData)
151 : {
152 18 : other.pWorkingData = nullptr;
153 18 : }
154 :
155 : /************************************************************************/
156 : /* Step operator=(Step&& other) */
157 : /************************************************************************/
158 :
159 : /** Move assignment operator */
160 0 : VRTProcessedDataset::Step &VRTProcessedDataset::Step::operator=(Step &&other)
161 : {
162 0 : if (&other != this)
163 : {
164 0 : deinit();
165 0 : osAlgorithm = std::move(other.osAlgorithm);
166 0 : aosArguments = std::move(other.aosArguments);
167 0 : eInDT = other.eInDT;
168 0 : eOutDT = other.eOutDT;
169 0 : nInBands = other.nInBands;
170 0 : nOutBands = other.nOutBands;
171 0 : adfInNoData = std::move(other.adfInNoData);
172 0 : adfOutNoData = std::move(other.adfOutNoData);
173 0 : std::swap(pWorkingData, other.pWorkingData);
174 : }
175 0 : return *this;
176 : }
177 :
178 : /************************************************************************/
179 : /* VRTProcessedDataset() */
180 : /************************************************************************/
181 :
182 : /** Constructor */
183 48 : VRTProcessedDataset::VRTProcessedDataset(int nXSize, int nYSize)
184 48 : : VRTDataset(nXSize, nYSize)
185 : {
186 48 : }
187 :
188 : /************************************************************************/
189 : /* ~VRTProcessedDataset() */
190 : /************************************************************************/
191 :
192 96 : VRTProcessedDataset::~VRTProcessedDataset()
193 :
194 : {
195 48 : VRTProcessedDataset::FlushCache(true);
196 48 : VRTProcessedDataset::CloseDependentDatasets();
197 96 : }
198 :
199 : /************************************************************************/
200 : /* XMLInit() */
201 : /************************************************************************/
202 :
203 : /** Instantiate object from XML tree */
204 48 : CPLErr VRTProcessedDataset::XMLInit(const CPLXMLNode *psTree,
205 : const char *pszVRTPathIn)
206 :
207 : {
208 48 : if (Init(psTree, pszVRTPathIn, nullptr, nullptr, -1) != CE_None)
209 35 : return CE_Failure;
210 :
211 13 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
212 13 : const int nOvrCount = poSrcFirstBand->GetOverviewCount();
213 13 : for (int i = 0; i < nOvrCount; ++i)
214 : {
215 0 : auto poOvrDS = std::make_unique<VRTProcessedDataset>(0, 0);
216 0 : if (poOvrDS->Init(psTree, pszVRTPathIn, this, m_poSrcDS.get(), i) !=
217 : CE_None)
218 0 : break;
219 0 : m_apoOverviewDatasets.emplace_back(std::move(poOvrDS));
220 : }
221 :
222 13 : return CE_None;
223 : }
224 :
225 : /** Instantiate object from XML tree */
226 48 : CPLErr VRTProcessedDataset::Init(const CPLXMLNode *psTree,
227 : const char *pszVRTPathIn,
228 : const VRTProcessedDataset *poParentDS,
229 : GDALDataset *poParentSrcDS, int iOvrLevel)
230 :
231 : {
232 48 : const CPLXMLNode *psInput = CPLGetXMLNode(psTree, "Input");
233 48 : if (!psInput)
234 : {
235 1 : CPLError(CE_Failure, CPLE_AppDefined, "Input element missing");
236 1 : return CE_Failure;
237 : }
238 :
239 47 : if (pszVRTPathIn)
240 2 : m_osVRTPath = pszVRTPathIn;
241 :
242 47 : if (poParentSrcDS)
243 : {
244 0 : m_poSrcDS.reset(
245 : GDALCreateOverviewDataset(poParentSrcDS, iOvrLevel, true));
246 : }
247 47 : else if (const CPLXMLNode *psSourceFileNameNode =
248 47 : CPLGetXMLNode(psInput, "SourceFilename"))
249 : {
250 45 : const bool bRelativeToVRT = CPL_TO_BOOL(
251 : atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0")));
252 : const std::string osFilename = VRTDataset::BuildSourceFilename(
253 : CPLGetXMLValue(psInput, "SourceFilename", ""), pszVRTPathIn,
254 90 : bRelativeToVRT);
255 45 : m_poSrcDS.reset(GDALDataset::Open(
256 : osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
257 : nullptr, nullptr));
258 : }
259 2 : else if (const CPLXMLNode *psVRTDataset =
260 2 : CPLGetXMLNode(psInput, "VRTDataset"))
261 : {
262 1 : CPLXMLNode sVRTDatasetTmp = *psVRTDataset;
263 1 : sVRTDatasetTmp.psNext = nullptr;
264 1 : char *pszXML = CPLSerializeXMLTree(&sVRTDatasetTmp);
265 1 : m_poSrcDS.reset(VRTDataset::OpenXML(pszXML, pszVRTPathIn, GA_ReadOnly));
266 1 : CPLFree(pszXML);
267 : }
268 : else
269 : {
270 1 : CPLError(
271 : CE_Failure, CPLE_AppDefined,
272 : "Input element should have a SourceFilename or VRTDataset element");
273 1 : return CE_Failure;
274 : }
275 :
276 46 : if (!m_poSrcDS)
277 2 : return CE_Failure;
278 :
279 44 : if (nRasterXSize == 0 && nRasterYSize == 0)
280 : {
281 42 : nRasterXSize = m_poSrcDS->GetRasterXSize();
282 42 : nRasterYSize = m_poSrcDS->GetRasterYSize();
283 : }
284 2 : else if (nRasterXSize != m_poSrcDS->GetRasterXSize() ||
285 0 : nRasterYSize != m_poSrcDS->GetRasterYSize())
286 : {
287 2 : CPLError(CE_Failure, CPLE_AppDefined,
288 : "Inconsistent declared VRT dimensions with input dataset");
289 2 : return CE_Failure;
290 : }
291 :
292 42 : if (m_poSrcDS->GetRasterCount() == 0)
293 0 : return CE_Failure;
294 :
295 : // Inherit SRS from source if not explicitly defined in VRT
296 42 : if (!CPLGetXMLNode(psTree, "SRS"))
297 : {
298 42 : const OGRSpatialReference *poSRS = m_poSrcDS->GetSpatialRef();
299 42 : if (poSRS)
300 : {
301 0 : m_poSRS.reset(poSRS->Clone());
302 : }
303 : }
304 :
305 : // Inherit GeoTransform from source if not explicitly defined in VRT
306 42 : if (iOvrLevel < 0 && !CPLGetXMLNode(psTree, "GeoTransform"))
307 : {
308 42 : if (m_poSrcDS->GetGeoTransform(m_adfGeoTransform) == CE_None)
309 22 : m_bGeoTransformSet = true;
310 : }
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Initialize blocksize before calling sub-init so that the */
314 : /* band initializers can get it from the dataset object when */
315 : /* they are created. */
316 : /* -------------------------------------------------------------------- */
317 :
318 42 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
319 42 : poSrcFirstBand->GetBlockSize(&m_nBlockXSize, &m_nBlockYSize);
320 42 : if (const char *pszBlockXSize =
321 42 : CPLGetXMLValue(psTree, "BlockXSize", nullptr))
322 0 : m_nBlockXSize = atoi(pszBlockXSize);
323 42 : if (const char *pszBlockYSize =
324 42 : CPLGetXMLValue(psTree, "BlockYSize", nullptr))
325 0 : m_nBlockYSize = atoi(pszBlockYSize);
326 :
327 : // Initialize all the general VRT stuff.
328 42 : if (VRTDataset::XMLInit(psTree, pszVRTPathIn) != CE_None)
329 : {
330 0 : return CE_Failure;
331 : }
332 :
333 : // Use geotransform from parent for overviews
334 42 : if (iOvrLevel >= 0 && poParentDS->m_bGeoTransformSet)
335 : {
336 0 : m_bGeoTransformSet = true;
337 0 : m_adfGeoTransform[0] = poParentDS->m_adfGeoTransform[0];
338 0 : m_adfGeoTransform[1] = poParentDS->m_adfGeoTransform[1];
339 0 : m_adfGeoTransform[2] = poParentDS->m_adfGeoTransform[2];
340 0 : m_adfGeoTransform[3] = poParentDS->m_adfGeoTransform[3];
341 0 : m_adfGeoTransform[4] = poParentDS->m_adfGeoTransform[4];
342 0 : m_adfGeoTransform[5] = poParentDS->m_adfGeoTransform[5];
343 :
344 0 : m_adfGeoTransform[1] *=
345 0 : static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
346 0 : m_adfGeoTransform[2] *=
347 0 : static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
348 0 : m_adfGeoTransform[4] *=
349 0 : static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
350 0 : m_adfGeoTransform[5] *=
351 0 : static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
352 : }
353 :
354 : // Create bands automatically from source dataset if not explicitly defined
355 : // in VRT.
356 42 : if (!CPLGetXMLNode(psTree, "VRTRasterBand"))
357 : {
358 143 : for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
359 : {
360 103 : const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
361 : auto poBand = new VRTProcessedRasterBand(
362 103 : this, i + 1, poSrcBand->GetRasterDataType());
363 103 : poBand->CopyCommonInfoFrom(poSrcBand);
364 103 : SetBand(i + 1, poBand);
365 : }
366 : }
367 :
368 : const CPLXMLNode *psProcessingSteps =
369 42 : CPLGetXMLNode(psTree, "ProcessingSteps");
370 42 : if (!psProcessingSteps)
371 : {
372 1 : CPLError(CE_Failure, CPLE_AppDefined,
373 : "ProcessingSteps element missing");
374 1 : return CE_Failure;
375 : }
376 :
377 41 : const auto eInDT = poSrcFirstBand->GetRasterDataType();
378 102 : for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
379 : {
380 61 : const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
381 61 : if (eDT != eInDT)
382 : {
383 0 : CPLError(CE_Warning, CPLE_AppDefined,
384 : "Not all bands of the input dataset have the same data "
385 : "type. The data type of the first band will be used as "
386 : "the reference one.");
387 0 : break;
388 : }
389 : }
390 :
391 41 : GDALDataType eCurrentDT = eInDT;
392 41 : int nCurrentBandCount = m_poSrcDS->GetRasterCount();
393 :
394 82 : std::vector<double> adfNoData;
395 143 : for (int i = 1; i <= nCurrentBandCount; ++i)
396 : {
397 102 : int bHasVal = FALSE;
398 : const double dfVal =
399 102 : m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
400 : adfNoData.emplace_back(
401 102 : bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
402 : }
403 :
404 41 : int nStepCount = 0;
405 83 : for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
406 42 : psStep = psStep->psNext)
407 : {
408 42 : if (psStep->eType == CXT_Element &&
409 42 : strcmp(psStep->pszValue, "Step") == 0)
410 : {
411 42 : ++nStepCount;
412 : }
413 : }
414 :
415 41 : int iStep = 0;
416 56 : for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
417 15 : psStep = psStep->psNext)
418 : {
419 42 : if (psStep->eType == CXT_Element &&
420 42 : strcmp(psStep->pszValue, "Step") == 0)
421 : {
422 42 : ++iStep;
423 42 : const bool bIsFinalStep = (iStep == nStepCount);
424 42 : std::vector<double> adfOutNoData;
425 42 : if (bIsFinalStep)
426 : {
427 : // Initialize adfOutNoData with nodata value of *output* bands
428 : // for final step
429 139 : for (int i = 1; i <= nBands; ++i)
430 : {
431 99 : int bHasVal = FALSE;
432 : const double dfVal =
433 99 : GetRasterBand(i)->GetNoDataValue(&bHasVal);
434 : adfOutNoData.emplace_back(
435 105 : bHasVal ? dfVal
436 105 : : std::numeric_limits<double>::quiet_NaN());
437 : }
438 : }
439 42 : if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
440 : adfNoData, adfOutNoData))
441 27 : return CE_Failure;
442 15 : adfNoData = std::move(adfOutNoData);
443 : }
444 : }
445 :
446 14 : if (m_aoSteps.empty())
447 : {
448 1 : CPLError(CE_Failure, CPLE_AppDefined,
449 : "At least one step should be defined");
450 1 : return CE_Failure;
451 : }
452 :
453 13 : if (nCurrentBandCount != nBands)
454 : {
455 0 : CPLError(CE_Failure, CPLE_AppDefined,
456 : "Number of output bands of last step is not consistent with "
457 : "number of VRTProcessedRasterBand's");
458 0 : return CE_Failure;
459 : }
460 :
461 13 : if (nBands > 1)
462 10 : SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
463 :
464 13 : m_oXMLTree.reset(CPLCloneXMLTree(psTree));
465 :
466 13 : return CE_None;
467 : }
468 :
469 : /************************************************************************/
470 : /* ParseStep() */
471 : /************************************************************************/
472 :
473 : /** Parse the current Step node and create a corresponding entry in m_aoSteps.
474 : *
475 : * @param psStep Step node
476 : * @param bIsFinalStep Whether this is the final step.
477 : * @param[in,out] eCurrentDT Input data type for this step.
478 : * Updated to output data type at end of method.
479 : * @param[in,out] nCurrentBandCount Input band count for this step.
480 : * Updated to output band cout at end of
481 : * method.
482 : * @param adfInNoData Input nodata values
483 : * @param[in,out] adfOutNoData Output nodata values, to be filled by this
484 : * method. When bIsFinalStep, this is also an
485 : * input parameter.
486 : * @return true on success.
487 : */
488 42 : bool VRTProcessedDataset::ParseStep(const CPLXMLNode *psStep, bool bIsFinalStep,
489 : GDALDataType &eCurrentDT,
490 : int &nCurrentBandCount,
491 : std::vector<double> &adfInNoData,
492 : std::vector<double> &adfOutNoData)
493 : {
494 42 : const char *pszStepName = CPLGetXMLValue(
495 42 : psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
496 42 : const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
497 42 : if (!pszAlgorithm)
498 : {
499 0 : CPLError(CE_Failure, CPLE_AppDefined,
500 : "Step '%s' lacks a Algorithm element", pszStepName);
501 0 : return false;
502 : }
503 :
504 42 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
505 42 : const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
506 42 : if (oIterFunc == oMapFunctions.end())
507 : {
508 0 : CPLError(CE_Failure, CPLE_AppDefined,
509 : "Step '%s' uses unregistered algorithm '%s'", pszStepName,
510 : pszAlgorithm);
511 0 : return false;
512 : }
513 :
514 42 : const auto &oFunc = oIterFunc->second;
515 :
516 42 : if (!oFunc.aeSupportedInputDT.empty())
517 : {
518 0 : if (std::find(oFunc.aeSupportedInputDT.begin(),
519 : oFunc.aeSupportedInputDT.end(),
520 0 : eCurrentDT) == oFunc.aeSupportedInputDT.end())
521 : {
522 0 : CPLError(CE_Failure, CPLE_AppDefined,
523 : "Step '%s' (using algorithm '%s') does not "
524 : "support input data type = '%s'",
525 : pszStepName, pszAlgorithm,
526 : GDALGetDataTypeName(eCurrentDT));
527 0 : return false;
528 : }
529 : }
530 :
531 42 : if (!oFunc.anSupportedInputBandCount.empty())
532 : {
533 0 : if (std::find(oFunc.anSupportedInputBandCount.begin(),
534 : oFunc.anSupportedInputBandCount.end(),
535 0 : nCurrentBandCount) ==
536 0 : oFunc.anSupportedInputBandCount.end())
537 : {
538 0 : CPLError(CE_Failure, CPLE_AppDefined,
539 : "Step '%s' (using algorithm '%s') does not "
540 : "support input band count = %d",
541 : pszStepName, pszAlgorithm, nCurrentBandCount);
542 0 : return false;
543 : }
544 : }
545 :
546 84 : Step oStep;
547 42 : oStep.osAlgorithm = pszAlgorithm;
548 84 : oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
549 42 : ? oFunc.eRequestedInputDT
550 : : eCurrentDT;
551 42 : oStep.nInBands = nCurrentBandCount;
552 :
553 : // Unless modified by pfnInit...
554 42 : oStep.eOutDT = oStep.eInDT;
555 :
556 42 : oStep.adfInNoData = adfInNoData;
557 42 : oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
558 :
559 : // Deal with constant arguments
560 42 : for (const auto &nameValuePair : oFunc.oMapConstantArguments)
561 : {
562 : oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
563 0 : nameValuePair.second.c_str());
564 : }
565 :
566 : // Deal with built-in arguments
567 42 : if (oFunc.oSetBuiltinArguments.find("nodata") !=
568 84 : oFunc.oSetBuiltinArguments.end())
569 : {
570 0 : int bHasVal = false;
571 0 : const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
572 0 : const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
573 0 : if (bHasVal)
574 : {
575 : oStep.aosArguments.AddNameValue("nodata",
576 0 : CPLSPrintf("%.18g", dfVal));
577 : }
578 : }
579 :
580 42 : if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
581 84 : oFunc.oSetBuiltinArguments.end())
582 : {
583 0 : for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
584 : {
585 0 : int bHasVal = false;
586 0 : const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
587 : oStep.aosArguments.AddNameValue(
588 : CPLSPrintf("offset_%d", i),
589 0 : CPLSPrintf("%.18g", bHasVal ? dfVal : 0.0));
590 : }
591 : }
592 :
593 42 : if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
594 84 : oFunc.oSetBuiltinArguments.end())
595 : {
596 0 : for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
597 : {
598 0 : int bHasVal = false;
599 0 : const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
600 : oStep.aosArguments.AddNameValue(
601 : CPLSPrintf("scale_%d", i),
602 0 : CPLSPrintf("%.18g", bHasVal ? dfVal : 1.0));
603 : }
604 : }
605 :
606 : // Parse arguments specified in VRT
607 84 : std::set<std::string> oFoundArguments;
608 :
609 246 : for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
610 204 : psStepChild = psStepChild->psNext)
611 : {
612 204 : if (psStepChild->eType == CXT_Element &&
613 194 : strcmp(psStepChild->pszValue, "Argument") == 0)
614 : {
615 : const char *pszParamName =
616 152 : CPLGetXMLValue(psStepChild, "name", nullptr);
617 152 : if (!pszParamName)
618 : {
619 0 : CPLError(CE_Failure, CPLE_AppDefined,
620 : "Step '%s' has a Argument without a name attribute",
621 : pszStepName);
622 0 : return false;
623 : }
624 152 : const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
625 : auto oOtherArgIter =
626 152 : oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
627 304 : if (!oFunc.oOtherArguments.empty() &&
628 304 : oOtherArgIter == oFunc.oOtherArguments.end())
629 : {
630 : // If we got a parameter name like 'coefficients_1',
631 : // try to fetch the generic 'coefficients_{band}'
632 154 : std::string osParamName(pszParamName);
633 77 : const auto nPos = osParamName.rfind('_');
634 77 : if (nPos != std::string::npos)
635 : {
636 77 : osParamName.resize(nPos + 1);
637 77 : osParamName += "{band}";
638 : oOtherArgIter = oFunc.oOtherArguments.find(
639 77 : CPLString(osParamName).tolower());
640 : }
641 : }
642 152 : if (oOtherArgIter != oFunc.oOtherArguments.end())
643 : {
644 152 : oFoundArguments.insert(oOtherArgIter->first);
645 :
646 152 : const std::string &osType = oOtherArgIter->second.osType;
647 152 : if (osType == "boolean")
648 : {
649 0 : if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
650 : {
651 0 : CPLError(CE_Failure, CPLE_NotSupported,
652 : "Step '%s' has a Argument '%s' whose "
653 : "value '%s' is not a boolean",
654 : pszStepName, pszParamName, pszValue);
655 0 : return false;
656 : }
657 : }
658 152 : else if (osType == "integer")
659 : {
660 36 : if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
661 : {
662 0 : CPLError(CE_Failure, CPLE_NotSupported,
663 : "Step '%s' has a Argument '%s' whose "
664 : "value '%s' is not a integer",
665 : pszStepName, pszParamName, pszValue);
666 0 : return false;
667 : }
668 : }
669 116 : else if (osType == "double")
670 : {
671 47 : const auto eType = CPLGetValueType(pszValue);
672 47 : if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
673 : {
674 0 : CPLError(CE_Failure, CPLE_NotSupported,
675 : "Step '%s' has a Argument '%s' whose "
676 : "value '%s' is not a double",
677 : pszStepName, pszParamName, pszValue);
678 0 : return false;
679 : }
680 : }
681 69 : else if (osType == "double_list")
682 : {
683 : const CPLStringList aosTokens(
684 25 : CSLTokenizeString2(pszValue, ",", 0));
685 114 : for (int i = 0; i < aosTokens.size(); ++i)
686 : {
687 89 : const auto eType = CPLGetValueType(aosTokens[i]);
688 89 : if (eType != CPL_VALUE_INTEGER &&
689 : eType != CPL_VALUE_REAL)
690 : {
691 0 : CPLError(CE_Failure, CPLE_NotSupported,
692 : "Step '%s' has a Argument '%s' "
693 : "whose value '%s' is not a "
694 : "comma-separated list of doubles",
695 : pszStepName, pszParamName, pszValue);
696 0 : return false;
697 : }
698 : }
699 : }
700 44 : else if (osType != "string")
701 : {
702 0 : CPLDebug("VRT", "Unhandled argument type '%s'",
703 : osType.c_str());
704 0 : CPLAssert(0);
705 : }
706 : }
707 0 : else if (oFunc.bMetadataSpecified &&
708 0 : oFunc.oSetBuiltinArguments.find(
709 0 : CPLString(pszParamName).tolower()) ==
710 0 : oFunc.oSetBuiltinArguments.end() &&
711 0 : oFunc.oMapConstantArguments.find(
712 0 : CPLString(pszParamName).tolower()) ==
713 0 : oFunc.oMapConstantArguments.end())
714 : {
715 0 : CPLError(CE_Warning, CPLE_NotSupported,
716 : "Step '%s' has a Argument '%s' which is not "
717 : "supported",
718 : pszStepName, pszParamName);
719 : }
720 :
721 152 : oStep.aosArguments.AddNameValue(pszParamName, pszValue);
722 : }
723 : }
724 :
725 : // Check that required arguments have been specified
726 361 : for (const auto &oIter : oFunc.oOtherArguments)
727 : {
728 429 : if (oIter.second.bRequired &&
729 429 : oFoundArguments.find(oIter.first) == oFoundArguments.end())
730 : {
731 3 : CPLError(CE_Failure, CPLE_AppDefined,
732 : "Step '%s' lacks required Argument '%s'", pszStepName,
733 : oIter.first.c_str());
734 3 : return false;
735 : }
736 : }
737 :
738 39 : if (oFunc.pfnInit)
739 : {
740 39 : double *padfOutNoData = nullptr;
741 39 : if (bIsFinalStep)
742 : {
743 37 : oStep.nOutBands = nBands;
744 37 : padfOutNoData =
745 37 : static_cast<double *>(CPLMalloc(nBands * sizeof(double)));
746 37 : CPLAssert(adfOutNoData.size() == static_cast<size_t>(nBands));
747 37 : memcpy(padfOutNoData, adfOutNoData.data(), nBands * sizeof(double));
748 : }
749 : else
750 : {
751 2 : oStep.nOutBands = 0;
752 : }
753 :
754 78 : if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
755 39 : oStep.aosArguments.List(), oStep.nInBands,
756 : oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
757 : &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
758 39 : &(oStep.pWorkingData)) != CE_None)
759 : {
760 24 : CPLError(CE_Failure, CPLE_AppDefined,
761 : "Step '%s' (using algorithm '%s') init() function "
762 : "failed",
763 : pszStepName, pszAlgorithm);
764 24 : CPLFree(padfOutNoData);
765 24 : return false;
766 : }
767 :
768 : // Input nodata values may have been modified by pfnInit()
769 15 : oStep.adfInNoData = adfInNoData;
770 :
771 15 : if (padfOutNoData)
772 : {
773 : adfOutNoData =
774 15 : std::vector<double>(padfOutNoData, padfOutNoData + nBands);
775 : }
776 : else
777 : {
778 0 : adfOutNoData = std::vector<double>(
779 0 : oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
780 : }
781 15 : CPLFree(padfOutNoData);
782 :
783 15 : oStep.adfOutNoData = adfOutNoData;
784 : }
785 : else
786 : {
787 0 : oStep.nOutBands = oStep.nInBands;
788 0 : adfOutNoData = oStep.adfOutNoData;
789 : }
790 :
791 15 : eCurrentDT = oStep.eOutDT;
792 15 : nCurrentBandCount = oStep.nOutBands;
793 :
794 15 : m_aoSteps.emplace_back(std::move(oStep));
795 :
796 15 : return true;
797 : }
798 :
799 : /************************************************************************/
800 : /* SerializeToXML() */
801 : /************************************************************************/
802 :
803 1 : CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
804 :
805 : {
806 1 : CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
807 1 : if (psTree == nullptr)
808 0 : return psTree;
809 :
810 : /* -------------------------------------------------------------------- */
811 : /* Remove VRTRasterBand nodes from the original tree and find the */
812 : /* last child. */
813 : /* -------------------------------------------------------------------- */
814 1 : CPLXMLNode *psLastChild = psTree->psChild;
815 1 : CPLXMLNode *psPrevChild = nullptr;
816 5 : while (psLastChild)
817 : {
818 5 : CPLXMLNode *psNextChild = psLastChild->psNext;
819 5 : if (psLastChild->eType == CXT_Element &&
820 3 : strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
821 : {
822 1 : if (psPrevChild)
823 1 : psPrevChild->psNext = psNextChild;
824 : else
825 0 : psTree->psChild = psNextChild;
826 1 : psLastChild->psNext = nullptr;
827 1 : CPLDestroyXMLNode(psLastChild);
828 1 : psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
829 : }
830 4 : else if (!psNextChild)
831 : {
832 1 : break;
833 : }
834 : else
835 : {
836 3 : psPrevChild = psLastChild;
837 3 : psLastChild = psNextChild;
838 : }
839 : }
840 1 : CPLAssert(psLastChild); // we have at least Input
841 :
842 : /* -------------------------------------------------------------------- */
843 : /* Serialize bands. */
844 : /* -------------------------------------------------------------------- */
845 1 : bool bHasWarnedAboutRAMUsage = false;
846 1 : size_t nAccRAMUsage = 0;
847 2 : for (int iBand = 0; iBand < nBands; iBand++)
848 : {
849 : CPLXMLNode *psBandTree =
850 1 : static_cast<VRTRasterBand *>(papoBands[iBand])
851 2 : ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
852 1 : nAccRAMUsage);
853 :
854 1 : if (psBandTree != nullptr)
855 : {
856 1 : psLastChild->psNext = psBandTree;
857 1 : psLastChild = psBandTree;
858 : }
859 : }
860 :
861 1 : return psTree;
862 : }
863 :
864 : /************************************************************************/
865 : /* SerializeToXML() */
866 : /************************************************************************/
867 :
868 : CPLXMLNode *
869 1 : VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
870 : bool &bHasWarnedAboutRAMUsage,
871 : size_t &nAccRAMUsage)
872 :
873 : {
874 1 : CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
875 : pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
876 :
877 : /* -------------------------------------------------------------------- */
878 : /* Set subclass. */
879 : /* -------------------------------------------------------------------- */
880 1 : CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
881 : CXT_Text, "VRTProcessedRasterBand");
882 :
883 1 : return psTree;
884 : }
885 :
886 : /************************************************************************/
887 : /* GetBlockSize() */
888 : /************************************************************************/
889 :
890 : /** Return block size */
891 105 : void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
892 : int *pnBlockYSize) const
893 :
894 : {
895 105 : *pnBlockXSize = m_nBlockXSize;
896 105 : *pnBlockYSize = m_nBlockYSize;
897 105 : }
898 :
899 : /************************************************************************/
900 : /* ProcessRegion() */
901 : /************************************************************************/
902 :
903 : /** Compute pixel values for the specified region.
904 : *
905 : * The output is stored in m_abyInput in a pixel-interleaved way.
906 : */
907 13 : bool VRTProcessedDataset::ProcessRegion(int nXOff, int nYOff, int nBufXSize,
908 : int nBufYSize)
909 : {
910 :
911 13 : CPLAssert(!m_aoSteps.empty());
912 :
913 13 : const int nFirstBandCount = m_aoSteps.front().nInBands;
914 13 : CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
915 13 : const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
916 13 : const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
917 13 : auto &abyInput = m_abyInput;
918 13 : auto &abyOutput = m_abyOutput;
919 : try
920 : {
921 13 : abyInput.resize(static_cast<size_t>(nBufXSize) * nBufYSize *
922 13 : nFirstBandCount * nFirstDTSize);
923 : }
924 0 : catch (const std::bad_alloc &)
925 : {
926 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
927 : "Out of memory allocating working buffer");
928 0 : return false;
929 : }
930 :
931 26 : if (m_poSrcDS->RasterIO(
932 13 : GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
933 : nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
934 13 : static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
935 13 : static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount * nBufXSize,
936 13 : nFirstDTSize, nullptr) != CE_None)
937 : {
938 0 : return false;
939 : }
940 :
941 13 : const double dfSrcXOff = nXOff;
942 13 : const double dfSrcYOff = nYOff;
943 13 : const double dfSrcXSize = nBufXSize;
944 13 : const double dfSrcYSize = nBufYSize;
945 :
946 : double adfSrcGT[6];
947 13 : if (m_poSrcDS->GetGeoTransform(adfSrcGT) != CE_None)
948 : {
949 10 : adfSrcGT[0] = 0;
950 10 : adfSrcGT[1] = 1;
951 10 : adfSrcGT[2] = 0;
952 10 : adfSrcGT[3] = 0;
953 10 : adfSrcGT[4] = 0;
954 10 : adfSrcGT[5] = 1;
955 : }
956 :
957 13 : GDALDataType eLastDT = eFirstDT;
958 13 : const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
959 28 : for (const auto &oStep : m_aoSteps)
960 : {
961 15 : const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
962 15 : CPLAssert(oIterFunc != oMapFunctions.end());
963 :
964 : // Data type adaptation
965 15 : if (eLastDT != oStep.eInDT)
966 : {
967 : try
968 : {
969 0 : abyOutput.resize(static_cast<size_t>(nBufXSize) * nBufYSize *
970 0 : oStep.nInBands *
971 0 : GDALGetDataTypeSizeBytes(oStep.eInDT));
972 : }
973 0 : catch (const std::bad_alloc &)
974 : {
975 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
976 : "Out of memory allocating working buffer");
977 0 : return false;
978 : }
979 :
980 0 : GDALCopyWords64(abyInput.data(), eLastDT,
981 0 : GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
982 0 : oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
983 0 : static_cast<size_t>(nBufXSize) * nBufYSize *
984 0 : oStep.nInBands);
985 :
986 0 : std::swap(abyInput, abyOutput);
987 : }
988 :
989 : try
990 : {
991 30 : abyOutput.resize(static_cast<size_t>(nBufXSize) * nBufYSize *
992 15 : oStep.nOutBands *
993 15 : GDALGetDataTypeSizeBytes(oStep.eOutDT));
994 : }
995 0 : catch (const std::bad_alloc &)
996 : {
997 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
998 : "Out of memory allocating working buffer");
999 0 : return false;
1000 : }
1001 :
1002 15 : const auto &oFunc = oIterFunc->second;
1003 60 : if (oFunc.pfnProcess(
1004 15 : oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
1005 : oStep.aosArguments.List(), nBufXSize, nBufYSize,
1006 15 : abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
1007 15 : oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
1008 15 : oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
1009 : dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, adfSrcGT,
1010 : m_osVRTPath.c_str(),
1011 15 : /*papszExtra=*/nullptr) != CE_None)
1012 : {
1013 0 : return false;
1014 : }
1015 :
1016 15 : std::swap(abyInput, abyOutput);
1017 15 : eLastDT = oStep.eOutDT;
1018 : }
1019 :
1020 13 : return true;
1021 : }
1022 :
1023 : /************************************************************************/
1024 : /* VRTProcessedRasterBand() */
1025 : /************************************************************************/
1026 :
1027 : /** Constructor */
1028 105 : VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
1029 : int nBandIn,
1030 105 : GDALDataType eDataTypeIn)
1031 : {
1032 105 : Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
1033 :
1034 105 : poDS = poDSIn;
1035 105 : nBand = nBandIn;
1036 105 : eAccess = GA_Update;
1037 105 : eDataType = eDataTypeIn;
1038 :
1039 105 : poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
1040 105 : }
1041 :
1042 : /************************************************************************/
1043 : /* GetOverviewCount() */
1044 : /************************************************************************/
1045 :
1046 0 : int VRTProcessedRasterBand::GetOverviewCount()
1047 : {
1048 0 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1049 0 : return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
1050 : }
1051 :
1052 : /************************************************************************/
1053 : /* GetOverview() */
1054 : /************************************************************************/
1055 :
1056 0 : GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
1057 : {
1058 0 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1059 0 : if (iOvr < 0 ||
1060 0 : iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
1061 0 : return nullptr;
1062 0 : return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
1063 : }
1064 :
1065 : /************************************************************************/
1066 : /* IReadBlock() */
1067 : /************************************************************************/
1068 :
1069 13 : CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1070 : void *pImage)
1071 :
1072 : {
1073 13 : auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
1074 :
1075 13 : int nBufXSize = 0;
1076 13 : int nBufYSize = 0;
1077 13 : GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
1078 :
1079 13 : const int nXPixelOff = nBlockXOff * nBlockXSize;
1080 13 : const int nYPixelOff = nBlockYOff * nBlockYSize;
1081 13 : if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize))
1082 : {
1083 0 : return CE_Failure;
1084 : }
1085 :
1086 13 : const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
1087 13 : CPLAssert(nOutBands == poVRTDS->GetRasterCount());
1088 13 : const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
1089 13 : const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
1090 13 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1091 :
1092 : // Dispatch final output buffer to cached blocks of output bands
1093 40 : for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
1094 : {
1095 27 : GDALRasterBlock *poBlock = nullptr;
1096 : GByte *pDst;
1097 27 : if (iDstBand + 1 == nBand)
1098 : {
1099 13 : pDst = static_cast<GByte *>(pImage);
1100 : }
1101 : else
1102 : {
1103 14 : auto poOtherBand = poVRTDS->papoBands[iDstBand];
1104 14 : poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1105 14 : if (poBlock)
1106 : {
1107 0 : poBlock->DropLock();
1108 0 : continue;
1109 : }
1110 14 : poBlock = poOtherBand->GetLockedBlockRef(
1111 : nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
1112 14 : if (!poBlock)
1113 0 : continue;
1114 14 : pDst = static_cast<GByte *>(poBlock->GetDataRef());
1115 : }
1116 67 : for (int iY = 0; iY < nBufYSize; ++iY)
1117 : {
1118 80 : GDALCopyWords(poVRTDS->m_abyInput.data() +
1119 40 : (iDstBand + static_cast<size_t>(iY) * nBufXSize *
1120 40 : nOutBands) *
1121 40 : nLastDTSize,
1122 : eLastDT, nLastDTSize * nOutBands,
1123 40 : pDst +
1124 40 : static_cast<size_t>(iY) * nBlockXSize * nDTSize,
1125 : eDataType, nDTSize, nBufXSize);
1126 : }
1127 27 : if (poBlock)
1128 14 : poBlock->DropLock();
1129 : }
1130 :
1131 13 : return CE_None;
1132 : }
1133 :
1134 : /*! @endcond */
1135 :
1136 : /************************************************************************/
1137 : /* GDALVRTRegisterProcessedDatasetFunc() */
1138 : /************************************************************************/
1139 :
1140 : /** Register a function to be used by VRTProcessedDataset.
1141 :
1142 : An example of content for pszXMLMetadata is:
1143 : \verbatim
1144 : <ProcessedDatasetFunctionArgumentsList>
1145 : <Argument name='src_nodata' type='double' description='Override input nodata value'/>
1146 : <Argument name='dst_nodata' type='double' description='Override output nodata value'/>
1147 : <Argument name='replacement_nodata' description='value to substitute to a valid computed value that would be nodata' type='double'/>
1148 : <Argument name='dst_intended_datatype' type='string' description='Intented datatype of output (which might be different than the working data type)'/>
1149 : <Argument name='coefficients_{band}' description='Comma-separated coefficients for combining bands. First one is constant term' type='double_list' required='true'/>
1150 : </ProcessedDatasetFunctionArgumentsList>
1151 : \endverbatim
1152 :
1153 : @param pszFuncName Function name. Must be unique and not null.
1154 : @param pUserData User data. May be nullptr. Must remain valid during the
1155 : lifetime of GDAL.
1156 : @param pszXMLMetadata XML metadata describing the function arguments. May be
1157 : nullptr if there are no arguments.
1158 : @param eRequestedInputDT If the pfnProcess callback only supports a single
1159 : data type, it should be specified in this parameter.
1160 : Otherwise set it to GDT_Unknown.
1161 : @param paeSupportedInputDT List of supported input data types. May be nullptr
1162 : if all are supported or if eRequestedInputDT is
1163 : set to a non GDT_Unknown value.
1164 : @param nSupportedInputDTSize Size of paeSupportedInputDT
1165 : @param panSupportedInputBandCount List of supported band count. May be nullptr
1166 : if any source band count is supported.
1167 : @param nSupportedInputBandCountSize Size of panSupportedInputBandCount
1168 : @param pfnInit Initialization function called when a VRTProcessedDataset
1169 : step uses the register function. This initialization function
1170 : will return the output data type, output band count and
1171 : potentially initialize a working structure, typically parsing
1172 : arguments. May be nullptr.
1173 : If not specified, it will be assumed that the input and output
1174 : data types are the same, and that the input number of bands
1175 : and output number of bands are the same.
1176 : @param pfnFree Free function that will free the working structure allocated
1177 : by pfnInit. May be nullptr.
1178 : @param pfnProcess Processing function called to compute pixel values. Must
1179 : not be nullptr.
1180 : @param papszOptions Unused currently. Must be nullptr.
1181 : @return CE_None in case of success, error otherwise.
1182 : @since 3.9
1183 : */
1184 4872 : CPLErr GDALVRTRegisterProcessedDatasetFunc(
1185 : const char *pszFuncName, void *pUserData, const char *pszXMLMetadata,
1186 : GDALDataType eRequestedInputDT, const GDALDataType *paeSupportedInputDT,
1187 : size_t nSupportedInputDTSize, const int *panSupportedInputBandCount,
1188 : size_t nSupportedInputBandCountSize,
1189 : GDALVRTProcessedDatasetFuncInit pfnInit,
1190 : GDALVRTProcessedDatasetFuncFree pfnFree,
1191 : GDALVRTProcessedDatasetFuncProcess pfnProcess,
1192 : CPL_UNUSED CSLConstList papszOptions)
1193 : {
1194 4872 : if (pszFuncName == nullptr || pszFuncName[0] == '\0')
1195 : {
1196 0 : CPLError(CE_Failure, CPLE_AppDefined,
1197 : "pszFuncName should be non-empty");
1198 0 : return CE_Failure;
1199 : }
1200 :
1201 4872 : auto &oMap = GetGlobalMapProcessedDatasetFunc();
1202 4872 : if (oMap.find(pszFuncName) != oMap.end())
1203 : {
1204 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
1205 : pszFuncName);
1206 0 : return CE_Failure;
1207 : }
1208 :
1209 4872 : if (!pfnProcess)
1210 : {
1211 0 : CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
1212 0 : return CE_Failure;
1213 : }
1214 :
1215 9744 : VRTProcessedDatasetFunc oFunc;
1216 4872 : oFunc.osFuncName = pszFuncName;
1217 4872 : oFunc.pUserData = pUserData;
1218 4872 : if (pszXMLMetadata)
1219 : {
1220 4872 : oFunc.bMetadataSpecified = true;
1221 4872 : auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
1222 4872 : if (!psTree)
1223 : {
1224 0 : CPLError(CE_Failure, CPLE_AppDefined,
1225 : "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
1226 : pszFuncName);
1227 0 : return CE_Failure;
1228 : }
1229 4872 : const CPLXMLNode *psRoot = CPLGetXMLNode(
1230 : psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
1231 4872 : if (!psRoot)
1232 : {
1233 0 : CPLError(CE_Failure, CPLE_AppDefined,
1234 : "No root ProcessedDatasetFunctionArgumentsList element in "
1235 : "pszXMLMetadata=%s for %s",
1236 : pszXMLMetadata, pszFuncName);
1237 0 : return CE_Failure;
1238 : }
1239 41412 : for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
1240 36540 : psIter = psIter->psNext)
1241 : {
1242 36540 : if (psIter->eType == CXT_Element &&
1243 36540 : strcmp(psIter->pszValue, "Argument") == 0)
1244 : {
1245 36540 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
1246 36540 : if (!pszName)
1247 : {
1248 0 : CPLError(CE_Failure, CPLE_AppDefined,
1249 : "Missing Argument.name attribute in "
1250 : "pszXMLMetadata=%s for %s",
1251 : pszXMLMetadata, pszFuncName);
1252 0 : return CE_Failure;
1253 : }
1254 36540 : const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
1255 36540 : if (!pszType)
1256 : {
1257 0 : CPLError(CE_Failure, CPLE_AppDefined,
1258 : "Missing Argument.type attribute in "
1259 : "pszXMLMetadata=%s for %s",
1260 : pszXMLMetadata, pszFuncName);
1261 0 : return CE_Failure;
1262 : }
1263 36540 : if (strcmp(pszType, "constant") == 0)
1264 : {
1265 : const char *pszValue =
1266 0 : CPLGetXMLValue(psIter, "value", nullptr);
1267 0 : if (!pszValue)
1268 : {
1269 0 : CPLError(CE_Failure, CPLE_AppDefined,
1270 : "Missing Argument.value attribute in "
1271 : "pszXMLMetadata=%s for %s",
1272 : pszXMLMetadata, pszFuncName);
1273 0 : return CE_Failure;
1274 : }
1275 0 : oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
1276 0 : pszValue;
1277 : }
1278 36540 : else if (strcmp(pszType, "builtin") == 0)
1279 : {
1280 0 : if (EQUAL(pszName, "nodata") ||
1281 0 : EQUAL(pszName, "offset_{band}") ||
1282 0 : EQUAL(pszName, "scale_{band}"))
1283 : {
1284 0 : oFunc.oSetBuiltinArguments.insert(
1285 0 : CPLString(pszName).tolower());
1286 : }
1287 : else
1288 : {
1289 0 : CPLError(CE_Failure, CPLE_NotSupported,
1290 : "Unsupported builtin parameter name %s in "
1291 : "pszXMLMetadata=%s for %s. Only nodata, "
1292 : "offset_{band} and scale_{band} are supported",
1293 : pszName, pszXMLMetadata, pszFuncName);
1294 0 : return CE_Failure;
1295 : }
1296 : }
1297 36540 : else if (strcmp(pszType, "boolean") == 0 ||
1298 34104 : strcmp(pszType, "string") == 0 ||
1299 28014 : strcmp(pszType, "integer") == 0 ||
1300 21924 : strcmp(pszType, "double") == 0 ||
1301 1218 : strcmp(pszType, "double_list") == 0)
1302 : {
1303 36540 : VRTProcessedDatasetFunc::OtherArgument otherArgument;
1304 36540 : otherArgument.bRequired = CPLTestBool(
1305 : CPLGetXMLValue(psIter, "required", "false"));
1306 36540 : otherArgument.osType = pszType;
1307 73080 : oFunc.oOtherArguments[CPLString(pszName).tolower()] =
1308 109620 : std::move(otherArgument);
1309 : }
1310 : else
1311 : {
1312 0 : CPLError(CE_Failure, CPLE_NotSupported,
1313 : "Unsupported type for parameter %s in "
1314 : "pszXMLMetadata=%s for %s. Only boolean, string, "
1315 : "integer, double and double_list are supported",
1316 : pszName, pszXMLMetadata, pszFuncName);
1317 0 : return CE_Failure;
1318 : }
1319 : }
1320 : }
1321 : }
1322 4872 : oFunc.eRequestedInputDT = eRequestedInputDT;
1323 4872 : if (nSupportedInputDTSize)
1324 : {
1325 : oFunc.aeSupportedInputDT.insert(
1326 0 : oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
1327 0 : paeSupportedInputDT + nSupportedInputDTSize);
1328 : }
1329 4872 : if (nSupportedInputBandCountSize)
1330 : {
1331 : oFunc.anSupportedInputBandCount.insert(
1332 0 : oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
1333 0 : panSupportedInputBandCount + nSupportedInputBandCountSize);
1334 : }
1335 4872 : oFunc.pfnInit = pfnInit;
1336 4872 : oFunc.pfnFree = pfnFree;
1337 4872 : oFunc.pfnProcess = pfnProcess;
1338 :
1339 4872 : oMap[pszFuncName] = std::move(oFunc);
1340 :
1341 4872 : return CE_None;
1342 : }
|