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