LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_calc.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 240 257 93.4 %
Date: 2025-05-15 13:16:46 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "gdal raster calc" subcommand
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_calc.h"
      14             : 
      15             : #include "../frmts/vrt/gdal_vrt.h"
      16             : #include "../frmts/vrt/vrtdataset.h"
      17             : 
      18             : #include "cpl_float.h"
      19             : #include "cpl_vsi_virtual.h"
      20             : #include "gdal_priv.h"
      21             : #include "gdal_utils.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <array>
      25             : #include <optional>
      26             : 
      27             : //! @cond Doxygen_Suppress
      28             : 
      29             : #ifndef _
      30             : #define _(x) (x)
      31             : #endif
      32             : 
      33             : struct GDALCalcOptions
      34             : {
      35             :     GDALDataType dstType{GDT_Float64};
      36             :     bool checkSRS{true};
      37             :     bool checkExtent{true};
      38             : };
      39             : 
      40         101 : static bool MatchIsCompleteVariableNameWithNoIndex(const std::string &str,
      41             :                                                    size_t from, size_t to)
      42             : {
      43         101 :     if (to < str.size())
      44             :     {
      45             :         // If the character after the end of the match is:
      46             :         // * alphanumeric or _ : we've matched only part of a variable name
      47             :         // * [ : we've matched a variable that already has an index
      48             :         // * ( : we've matched a function name
      49         130 :         if (std::isalnum(str[to]) || str[to] == '_' || str[to] == '[' ||
      50          46 :             str[to] == '(')
      51             :         {
      52          39 :             return false;
      53             :         }
      54             :     }
      55          62 :     if (from > 0)
      56             :     {
      57             :         // If the character before the start of the match is alphanumeric or _,
      58             :         // we've matched only part of a variable name.
      59          39 :         if (std::isalnum(str[from - 1]) || str[from - 1] == '_')
      60             :         {
      61           3 :             return false;
      62             :         }
      63             :     }
      64             : 
      65          59 :     return true;
      66             : }
      67             : 
      68             : /**
      69             :  *  Add a band subscript to all instances of a specified variable that
      70             :  *  do not already have such a subscript. For example, "X" would be
      71             :  *  replaced with "X[3]" but "X[1]" would be left untouched.
      72             :  */
      73          65 : static std::string SetBandIndices(const std::string &origExpression,
      74             :                                   const std::string &variable, int band,
      75             :                                   bool &expressionChanged)
      76             : {
      77          65 :     std::string expression = origExpression;
      78          65 :     expressionChanged = false;
      79             : 
      80          65 :     std::string::size_type seekPos = 0;
      81          65 :     auto pos = expression.find(variable, seekPos);
      82         166 :     while (pos != std::string::npos)
      83             :     {
      84         101 :         auto end = pos + variable.size();
      85             : 
      86         101 :         if (MatchIsCompleteVariableNameWithNoIndex(expression, pos, end))
      87             :         {
      88             :             // No index specified for variable
      89         118 :             expression = expression.substr(0, pos + variable.size()) + '[' +
      90         177 :                          std::to_string(band) + ']' + expression.substr(end);
      91          59 :             expressionChanged = true;
      92             :         }
      93             : 
      94         101 :         seekPos = end;
      95         101 :         pos = expression.find(variable, seekPos);
      96             :     }
      97             : 
      98          65 :     return expression;
      99             : }
     100             : 
     101             : struct SourceProperties
     102             : {
     103             :     int nBands{0};
     104             :     int nX{0};
     105             :     int nY{0};
     106             :     std::array<double, 6> gt{};
     107             :     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> srs{
     108             :         nullptr};
     109             : };
     110             : 
     111             : static std::optional<SourceProperties>
     112          55 : UpdateSourceProperties(SourceProperties &out, const std::string &dsn,
     113             :                        const GDALCalcOptions &options)
     114             : {
     115         110 :     SourceProperties source;
     116          55 :     bool srsMismatch = false;
     117          55 :     bool extentMismatch = false;
     118          55 :     bool dimensionMismatch = false;
     119             : 
     120             :     {
     121             :         std::unique_ptr<GDALDataset> ds(
     122          55 :             GDALDataset::Open(dsn.c_str(), GDAL_OF_RASTER));
     123             : 
     124          55 :         if (!ds)
     125             :         {
     126           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to open %s",
     127             :                      dsn.c_str());
     128           0 :             return std::nullopt;
     129             :         }
     130             : 
     131          55 :         source.nBands = ds->GetRasterCount();
     132          55 :         source.nX = ds->GetRasterXSize();
     133          55 :         source.nY = ds->GetRasterYSize();
     134             : 
     135          55 :         if (options.checkExtent)
     136             :         {
     137          51 :             ds->GetGeoTransform(source.gt.data());
     138             :         }
     139             : 
     140          55 :         if (options.checkSRS && out.srs)
     141             :         {
     142          12 :             const OGRSpatialReference *srs = ds->GetSpatialRef();
     143          12 :             srsMismatch = srs && !srs->IsSame(out.srs.get());
     144             :         }
     145             :     }
     146             : 
     147          55 :     if (source.nX != out.nX || source.nY != out.nY)
     148             :     {
     149           3 :         dimensionMismatch = true;
     150             :     }
     151             : 
     152         110 :     if (source.gt[0] != out.gt[0] || source.gt[2] != out.gt[2] ||
     153         110 :         source.gt[3] != out.gt[3] || source.gt[4] != out.gt[4])
     154             :     {
     155           5 :         extentMismatch = true;
     156             :     }
     157          55 :     if (source.gt[1] != out.gt[1] || source.gt[5] != out.gt[5])
     158             :     {
     159             :         // Resolutions are different. Are the extents the same?
     160           8 :         double xmaxOut = out.gt[0] + out.nX * out.gt[1] + out.nY * out.gt[2];
     161           8 :         double yminOut = out.gt[3] + out.nX * out.gt[4] + out.nY * out.gt[5];
     162             : 
     163             :         double xmax =
     164           8 :             source.gt[0] + source.nX * source.gt[1] + source.nY * source.gt[2];
     165             :         double ymin =
     166           8 :             source.gt[3] + source.nX * source.gt[4] + source.nY * source.gt[5];
     167             : 
     168             :         // Max allowable extent misalignment, expressed as fraction of a pixel
     169           8 :         constexpr double EXTENT_RTOL = 1e-3;
     170             : 
     171          11 :         if (std::abs(xmax - xmaxOut) > EXTENT_RTOL * std::abs(source.gt[1]) ||
     172           3 :             std::abs(ymin - yminOut) > EXTENT_RTOL * std::abs(source.gt[5]))
     173             :         {
     174           5 :             extentMismatch = true;
     175             :         }
     176             :     }
     177             : 
     178          55 :     if (options.checkExtent && extentMismatch)
     179             :     {
     180           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     181             :                  "Input extents are inconsistent.");
     182           1 :         return std::nullopt;
     183             :     }
     184             : 
     185          54 :     if (!options.checkExtent && dimensionMismatch)
     186             :     {
     187           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     188             :                  "Inputs do not have the same dimensions.");
     189           1 :         return std::nullopt;
     190             :     }
     191             : 
     192             :     // Find a common resolution
     193          53 :     if (source.nX > out.nX)
     194             :     {
     195           1 :         auto dx = CPLGreatestCommonDivisor(out.gt[1], source.gt[1]);
     196           1 :         if (dx == 0)
     197             :         {
     198           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     199             :                      "Failed to find common resolution for inputs.");
     200           0 :             return std::nullopt;
     201             :         }
     202           1 :         out.nX = static_cast<int>(
     203           1 :             std::round(static_cast<double>(out.nX) * out.gt[1] / dx));
     204           1 :         out.gt[1] = dx;
     205             :     }
     206          53 :     if (source.nY > out.nY)
     207             :     {
     208           1 :         auto dy = CPLGreatestCommonDivisor(out.gt[5], source.gt[5]);
     209           1 :         if (dy == 0)
     210             :         {
     211           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     212             :                      "Failed to find common resolution for inputs.");
     213           0 :             return std::nullopt;
     214             :         }
     215           1 :         out.nY = static_cast<int>(
     216           1 :             std::round(static_cast<double>(out.nY) * out.gt[5] / dy));
     217           1 :         out.gt[5] = dy;
     218             :     }
     219             : 
     220          53 :     if (srsMismatch)
     221             :     {
     222           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     223             :                  "Input spatial reference systems are inconsistent.");
     224           1 :         return std::nullopt;
     225             :     }
     226             : 
     227          52 :     return source;
     228             : }
     229             : 
     230             : /** Create XML nodes for one or more derived bands resulting from the evaluation
     231             :  *  of a single expression
     232             :  *
     233             :  * @param root VRTDataset node to which the band nodes should be added
     234             :  * @param nXOut Number of columns in VRT dataset
     235             :  * @param nYOut Number of rows in VRT dataset
     236             :  * @param expression Expression for which band(s) should be added
     237             :  * @param sources Mapping of source names to DSNs
     238             :  * @param sourceProps Mapping of source names to properties
     239             :  * @return true if the band(s) were added, false otherwise
     240             :  */
     241             : static bool
     242          38 : CreateDerivedBandXML(CPLXMLNode *root, int nXOut, int nYOut,
     243             :                      GDALDataType bandType, const std::string &expression,
     244             :                      const std::map<std::string, std::string> &sources,
     245             :                      const std::map<std::string, SourceProperties> &sourceProps)
     246             : {
     247          38 :     int nOutBands = 1;  // By default, each expression produces a single output
     248             :                         // band. When processing the expression below, we may
     249             :                         // discover that the expression produces multiple bands,
     250             :                         // in which case this will be updated.
     251          84 :     for (int nOutBand = 1; nOutBand <= nOutBands; nOutBand++)
     252             :     {
     253             :         // Copy the expression for each output band, because we may modify it
     254             :         // when adding band indices (e.g., X -> X[1]) to the variables in the
     255             :         // expression.
     256          48 :         std::string bandExpression = expression;
     257             : 
     258          48 :         CPLXMLNode *band = CPLCreateXMLNode(root, CXT_Element, "VRTRasterBand");
     259          48 :         CPLAddXMLAttributeAndValue(band, "subClass", "VRTDerivedRasterBand");
     260          48 :         CPLAddXMLAttributeAndValue(band, "dataType",
     261             :                                    GDALGetDataTypeName(bandType));
     262             : 
     263             :         CPLXMLNode *sourceTransferType =
     264          48 :             CPLCreateXMLNode(band, CXT_Element, "SourceTransferType");
     265          48 :         CPLCreateXMLNode(sourceTransferType, CXT_Text,
     266             :                          GDALGetDataTypeName(GDT_Float64));
     267             : 
     268             :         CPLXMLNode *pixelFunctionType =
     269          48 :             CPLCreateXMLNode(band, CXT_Element, "PixelFunctionType");
     270          48 :         CPLCreateXMLNode(pixelFunctionType, CXT_Text, "expression");
     271             :         CPLXMLNode *arguments =
     272          48 :             CPLCreateXMLNode(band, CXT_Element, "PixelFunctionArguments");
     273             : 
     274         111 :         for (const auto &[source_name, dsn] : sources)
     275             :         {
     276          65 :             auto it = sourceProps.find(source_name);
     277          65 :             CPLAssert(it != sourceProps.end());
     278          65 :             const auto &props = it->second;
     279             : 
     280             :             {
     281          65 :                 const int nDefaultInBand = std::min(props.nBands, nOutBand);
     282             : 
     283          65 :                 CPLString expressionBandVariable;
     284             :                 expressionBandVariable.Printf("%s[%d]", source_name.c_str(),
     285          65 :                                               nDefaultInBand);
     286             : 
     287          65 :                 bool expressionUsesAllBands = false;
     288             :                 bandExpression =
     289         130 :                     SetBandIndices(bandExpression, source_name, nDefaultInBand,
     290          65 :                                    expressionUsesAllBands);
     291             : 
     292          65 :                 if (expressionUsesAllBands)
     293             :                 {
     294          51 :                     if (nOutBands <= 1)
     295             :                     {
     296          37 :                         nOutBands = props.nBands;
     297             :                     }
     298          14 :                     else if (props.nBands != 1 && props.nBands != nOutBands)
     299             :                     {
     300           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
     301             :                                  "Expression cannot operate on all bands of "
     302             :                                  "rasters with incompatible numbers of bands "
     303             :                                  "(source %s has %d bands but expected to have "
     304             :                                  "1 or %d bands).",
     305           2 :                                  source_name.c_str(), props.nBands, nOutBands);
     306           2 :                         return false;
     307             :                     }
     308             :                 }
     309             :             }
     310             : 
     311             :             // Create a <SimpleSource> for each input band that is used in
     312             :             // the expression.
     313         170 :             for (int nInBand = 1; nInBand <= props.nBands; nInBand++)
     314             :             {
     315         107 :                 CPLString inBandVariable;
     316         107 :                 inBandVariable.Printf("%s[%d]", source_name.c_str(), nInBand);
     317         107 :                 if (bandExpression.find(inBandVariable) == std::string::npos)
     318             :                 {
     319          31 :                     continue;
     320             :                 }
     321             : 
     322             :                 CPLXMLNode *source =
     323          76 :                     CPLCreateXMLNode(band, CXT_Element, "SimpleSource");
     324          76 :                 CPLAddXMLAttributeAndValue(source, "name",
     325             :                                            inBandVariable.c_str());
     326             : 
     327             :                 CPLXMLNode *sourceFilename =
     328          76 :                     CPLCreateXMLNode(source, CXT_Element, "SourceFilename");
     329          76 :                 CPLAddXMLAttributeAndValue(sourceFilename, "relativeToVRT",
     330             :                                            "0");
     331          76 :                 CPLCreateXMLNode(sourceFilename, CXT_Text, dsn.c_str());
     332             : 
     333             :                 CPLXMLNode *sourceBand =
     334          76 :                     CPLCreateXMLNode(source, CXT_Element, "SourceBand");
     335          76 :                 CPLCreateXMLNode(sourceBand, CXT_Text,
     336         152 :                                  std::to_string(nInBand).c_str());
     337             : 
     338             :                 // TODO add <SourceProperties> ?
     339             : 
     340             :                 CPLXMLNode *srcRect =
     341          76 :                     CPLCreateXMLNode(source, CXT_Element, "SrcRect");
     342          76 :                 CPLAddXMLAttributeAndValue(srcRect, "xOff", "0");
     343          76 :                 CPLAddXMLAttributeAndValue(srcRect, "yOff", "0");
     344          76 :                 CPLAddXMLAttributeAndValue(srcRect, "xSize",
     345         152 :                                            std::to_string(props.nX).c_str());
     346          76 :                 CPLAddXMLAttributeAndValue(srcRect, "ySize",
     347         152 :                                            std::to_string(props.nY).c_str());
     348             : 
     349             :                 CPLXMLNode *dstRect =
     350          76 :                     CPLCreateXMLNode(source, CXT_Element, "DstRect");
     351          76 :                 CPLAddXMLAttributeAndValue(dstRect, "xOff", "0");
     352          76 :                 CPLAddXMLAttributeAndValue(dstRect, "yOff", "0");
     353          76 :                 CPLAddXMLAttributeAndValue(dstRect, "xSize",
     354         152 :                                            std::to_string(nXOut).c_str());
     355          76 :                 CPLAddXMLAttributeAndValue(dstRect, "ySize",
     356         152 :                                            std::to_string(nYOut).c_str());
     357             :             }
     358             :         }
     359             : 
     360             :         // Add the expression as a last step, because we may modify the
     361             :         // expression as we iterate through the bands.
     362          46 :         CPLAddXMLAttributeAndValue(arguments, "expression",
     363             :                                    bandExpression.c_str());
     364          46 :         CPLAddXMLAttributeAndValue(arguments, "dialect", "muparser");
     365             :     }
     366             : 
     367          36 :     return true;
     368             : }
     369             : 
     370          40 : static bool ParseSourceDescriptors(const std::vector<std::string> &inputs,
     371             :                                    std::map<std::string, std::string> &datasets,
     372             :                                    std::string &firstSourceName)
     373             : {
     374          40 :     bool isFirst = true;
     375             : 
     376          97 :     for (const auto &input : inputs)
     377             :     {
     378          57 :         std::string name = "";
     379             : 
     380          57 :         auto pos = input.find('=');
     381          57 :         if (pos == std::string::npos)
     382             :         {
     383          10 :             if (inputs.size() > 1)
     384             :             {
     385           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     386             :                          "Inputs must be named when more than one input is "
     387             :                          "provided.");
     388           0 :                 return false;
     389             :             }
     390          10 :             name = "X";
     391             :         }
     392             :         else
     393             :         {
     394          47 :             name = input.substr(0, pos);
     395             :         }
     396             : 
     397             :         std::string dsn =
     398         114 :             (pos == std::string::npos) ? input : input.substr(pos + 1);
     399          57 :         datasets[name] = std::move(dsn);
     400             : 
     401          57 :         if (isFirst)
     402             :         {
     403          40 :             firstSourceName = name;
     404          40 :             isFirst = false;
     405             :         }
     406             :     }
     407             : 
     408          40 :     return true;
     409             : }
     410             : 
     411          40 : static bool ReadFileLists(std::vector<std::string> &inputs)
     412             : {
     413          97 :     for (std::size_t i = 0; i < inputs.size(); i++)
     414             :     {
     415          57 :         const auto &input = inputs[i];
     416          57 :         if (input[0] == '@')
     417             :         {
     418             :             auto f =
     419           1 :                 VSIVirtualHandleUniquePtr(VSIFOpenL(input.c_str() + 1, "r"));
     420           1 :             if (!f)
     421             :             {
     422           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     423           0 :                          input.c_str() + 1);
     424           0 :                 return false;
     425             :             }
     426           1 :             std::vector<std::string> sources;
     427           3 :             while (const char *filename = CPLReadLineL(f.get()))
     428             :             {
     429           2 :                 sources.push_back(filename);
     430           2 :             }
     431           1 :             inputs.erase(inputs.begin() + static_cast<int>(i));
     432           1 :             inputs.insert(inputs.end(), sources.begin(), sources.end());
     433             :         }
     434             :     }
     435             : 
     436          40 :     return true;
     437             : }
     438             : 
     439             : /** Creates a VRT datasource with one or more derived raster bands containing
     440             :  *  results of an expression.
     441             :  *
     442             :  * To make this work with muparser (which does not support vector types), we
     443             :  * do a simple parsing of the expression internally, transforming it into
     444             :  * multiple expressions with explicit band indices. For example, for a two-band
     445             :  * raster "X", the expression "X + 3" will be transformed into "X[1] + 3" and
     446             :  * "X[2] + 3". The use of brackets is for readability only; as far as the
     447             :  * expression engine is concerned, the variables "X[1]" and "X[2]" have nothing
     448             :  * to do with each other.
     449             :  *
     450             :  * @param inputs A list of sources, expressed as NAME=DSN
     451             :  * @param expressions A list of expressions to be evaluated
     452             :  * @param options flags controlling which checks should be performed on the inputs
     453             :  *
     454             :  * @return a newly created VRTDataset, or nullptr on error
     455             :  */
     456             : static std::unique_ptr<GDALDataset>
     457          40 : GDALCalcCreateVRTDerived(const std::vector<std::string> &inputs,
     458             :                          const std::vector<std::string> &expressions,
     459             :                          const GDALCalcOptions &options)
     460             : {
     461          40 :     if (inputs.empty())
     462             :     {
     463           0 :         return nullptr;
     464             :     }
     465             : 
     466          80 :     std::map<std::string, std::string> sources;
     467          80 :     std::string firstSource;
     468          40 :     if (!ParseSourceDescriptors(inputs, sources, firstSource))
     469             :     {
     470           0 :         return nullptr;
     471             :     }
     472             : 
     473             :     // Use the first source provided to determine properties of the output
     474          40 :     const char *firstDSN = sources[firstSource].c_str();
     475             : 
     476             :     // Read properties from the first source
     477          80 :     SourceProperties out;
     478             :     {
     479             :         std::unique_ptr<GDALDataset> ds(
     480          40 :             GDALDataset::Open(firstDSN, GDAL_OF_RASTER));
     481             : 
     482          40 :         if (!ds)
     483             :         {
     484           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to open %s",
     485             :                      firstDSN);
     486           0 :             return nullptr;
     487             :         }
     488             : 
     489          40 :         out.nX = ds->GetRasterXSize();
     490          40 :         out.nY = ds->GetRasterYSize();
     491          40 :         out.nBands = 1;
     492          40 :         out.srs.reset(ds->GetSpatialRef() ? ds->GetSpatialRef()->Clone()
     493             :                                           : nullptr);
     494          40 :         ds->GetGeoTransform(out.gt.data());
     495             :     }
     496             : 
     497          80 :     CPLXMLTreeCloser root(CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset"));
     498             : 
     499             :     // Collect properties of the different sources, and verity them for
     500             :     // consistency.
     501          80 :     std::map<std::string, SourceProperties> sourceProps;
     502          92 :     for (const auto &[source_name, dsn] : sources)
     503             :     {
     504             :         // TODO avoid opening the first source twice.
     505          55 :         auto props = UpdateSourceProperties(out, dsn, options);
     506          55 :         if (props.has_value())
     507             :         {
     508          52 :             sourceProps[source_name] = std::move(props.value());
     509             :         }
     510             :         else
     511             :         {
     512           3 :             return nullptr;
     513             :         }
     514             :     }
     515             : 
     516          73 :     for (const auto &origExpression : expressions)
     517             :     {
     518          38 :         if (!CreateDerivedBandXML(root.get(), out.nX, out.nY, options.dstType,
     519             :                                   origExpression, sources, sourceProps))
     520             :         {
     521           2 :             return nullptr;
     522             :         }
     523             :     }
     524             : 
     525             :     //CPLDebug("VRT", "%s", CPLSerializeXMLTree(root.get()));
     526             : 
     527          70 :     auto ds = std::make_unique<VRTDataset>(out.nX, out.nY);
     528          35 :     if (ds->XMLInit(root.get(), "") != CE_None)
     529             :     {
     530           0 :         return nullptr;
     531             :     };
     532          35 :     ds->SetGeoTransform(out.gt.data());
     533          35 :     if (out.srs)
     534             :     {
     535          12 :         ds->SetSpatialRef(out.srs.get());
     536             :     }
     537             : 
     538          35 :     return ds;
     539             : }
     540             : 
     541             : /************************************************************************/
     542             : /*          GDALRasterCalcAlgorithm::GDALRasterCalcAlgorithm()          */
     543             : /************************************************************************/
     544             : 
     545          38 : GDALRasterCalcAlgorithm::GDALRasterCalcAlgorithm() noexcept
     546          38 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
     547             : {
     548          38 :     m_supportsStreamedOutput = true;
     549             : 
     550          38 :     AddProgressArg();
     551             : 
     552          76 :     AddArg(GDAL_ARG_NAME_INPUT, 'i', _("Input raster datasets"), &m_inputs)
     553          38 :         .SetPositional()
     554          38 :         .SetMinCount(1)
     555          38 :         .SetAutoOpenDataset(false)
     556          38 :         .SetMetaVar("INPUTS");
     557             : 
     558             :     AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
     559          38 :                        /* bGDALGAllowed = */ true);
     560          38 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
     561          38 :     AddCreationOptionsArg(&m_creationOptions);
     562          38 :     AddOverwriteArg(&m_overwrite);
     563          38 :     AddOutputDataTypeArg(&m_type);
     564             : 
     565             :     AddArg("no-check-srs", 0,
     566             :            _("Do not check consistency of input spatial reference systems"),
     567          38 :            &m_NoCheckSRS);
     568             :     AddArg("no-check-extent", 0, _("Do not check consistency of input extents"),
     569          38 :            &m_NoCheckExtent);
     570             : 
     571          76 :     AddArg("calc", 0, _("Expression(s) to evaluate"), &m_expr)
     572          38 :         .SetRequired()
     573          38 :         .SetMinCount(1);
     574          38 : }
     575             : 
     576             : /************************************************************************/
     577             : /*                GDALRasterCalcAlgorithm::RunImpl()                    */
     578             : /************************************************************************/
     579             : 
     580          40 : bool GDALRasterCalcAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     581             :                                       void *pProgressData)
     582             : {
     583          40 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     584             : 
     585          40 :     GDALCalcOptions options;
     586          40 :     options.checkExtent = !m_NoCheckExtent;
     587          40 :     options.checkSRS = !m_NoCheckSRS;
     588          40 :     if (!m_type.empty())
     589             :     {
     590           1 :         options.dstType = GDALGetDataTypeByName(m_type.c_str());
     591             :     }
     592             : 
     593          40 :     if (!ReadFileLists(m_inputs))
     594             :     {
     595           0 :         return false;
     596             :     }
     597             : 
     598          80 :     auto vrt = GDALCalcCreateVRTDerived(m_inputs, m_expr, options);
     599          40 :     if (vrt == nullptr)
     600             :     {
     601           5 :         return false;
     602             :     }
     603             : 
     604          35 :     if (m_format == "stream")
     605             :     {
     606           1 :         m_outputDataset.Set(std::move(vrt));
     607           1 :         return true;
     608             :     }
     609             : 
     610          68 :     CPLStringList translateArgs;
     611          34 :     if (!m_format.empty())
     612             :     {
     613           2 :         translateArgs.AddString("-of");
     614           2 :         translateArgs.AddString(m_format.c_str());
     615             :     }
     616          35 :     for (const auto &co : m_creationOptions)
     617             :     {
     618           1 :         translateArgs.AddString("-co");
     619           1 :         translateArgs.AddString(co.c_str());
     620             :     }
     621             : 
     622             :     GDALTranslateOptions *translateOptions =
     623          34 :         GDALTranslateOptionsNew(translateArgs.List(), nullptr);
     624          34 :     GDALTranslateOptionsSetProgress(translateOptions, pfnProgress,
     625             :                                     pProgressData);
     626             : 
     627             :     auto poOutDS =
     628             :         std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALTranslate(
     629          34 :             m_outputDataset.GetName().c_str(), GDALDataset::ToHandle(vrt.get()),
     630          68 :             translateOptions, nullptr)));
     631          34 :     GDALTranslateOptionsFree(translateOptions);
     632             : 
     633          34 :     const bool bOK = poOutDS != nullptr;
     634          34 :     m_outputDataset.Set(std::move(poOutDS));
     635             : 
     636          34 :     return bOK;
     637             : }
     638             : 
     639             : //! @endcond

Generated by: LCOV version 1.14