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

Generated by: LCOV version 1.14