LCOV - code coverage report
Current view: top level - frmts/vrt - vrtprocesseddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 414 554 74.7 %
Date: 2024-11-21 22:18:42 Functions: 18 20 90.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             :  * 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             : }

Generated by: LCOV version 1.14