LCOV - code coverage report
Current view: top level - frmts/vrt - vrtprocesseddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 389 553 70.3 %
Date: 2024-05-07 17:03:27 Functions: 17 20 85.0 %

          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             : }

Generated by: LCOV version 1.14