LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_maths.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 268 282 95.0 %
Date: 2026-06-19 21:24:00 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_maths.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  Mathematic operations on GDALMDArray
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_multidim.h"
      15             : #include "gdal_pam_multidim.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : #include <cmath>
      19             : #include <limits>
      20             : 
      21             : /************************************************************************/
      22             : /*                    GDALMDArray::HasSameShapeAs()                     */
      23             : /************************************************************************/
      24             : 
      25             : /** Returns true if both arrays have the same shape.
      26             :  *
      27             :  * That is to say the same number of dimensions and the size of corresponding
      28             :  * dimensions is the same.
      29             :  *
      30             :  * @since 3.14
      31             :  */
      32         125 : bool GDALMDArray::HasSameShapeAs(const GDALMDArray &other) const
      33             : {
      34         125 :     bool bRet = (GetDimensionCount() == other.GetDimensionCount());
      35         125 :     if (bRet)
      36             :     {
      37         125 :         const auto &apoThisDims = GetDimensions();
      38         125 :         const auto &apoOtherDims = other.GetDimensions();
      39         268 :         for (size_t i = 0; bRet && i < apoThisDims.size(); ++i)
      40             :         {
      41         143 :             bRet = (apoThisDims[i]->GetSize() == apoOtherDims[i]->GetSize());
      42             :         }
      43             :     }
      44         125 :     return bRet;
      45             : }
      46             : 
      47             : /************************************************************************/
      48             : /*                       GDALMathOperationMDArray                       */
      49             : /************************************************************************/
      50             : 
      51             : //! @cond Doxygen_Suppress
      52             : 
      53             : class GDALMathOperationMDArray final : public GDALPamMDArray
      54             : {
      55             :   public:
      56             :     enum class Operation
      57             :     {
      58             :         OP_ADD,
      59             :         OP_SUBTRACT,
      60             :         OP_MULTIPLY,
      61             :         OP_DIVIDE,
      62             :     };
      63             : 
      64         218 :     static const char *OperationToString(Operation op)
      65             :     {
      66         218 :         switch (op)
      67             :         {
      68          74 :             case Operation::OP_ADD:
      69          74 :                 break;
      70          66 :             case Operation::OP_SUBTRACT:
      71          66 :                 return "-";
      72          47 :             case Operation::OP_MULTIPLY:
      73          47 :                 return "*";
      74          31 :             case Operation::OP_DIVIDE:
      75          31 :                 return "/";
      76             :         }
      77          74 :         return "+";
      78             :     }
      79             : 
      80             :     static std::shared_ptr<GDALMathOperationMDArray>
      81             :     Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
      82             :            const std::shared_ptr<GDALMDArray> &arrayRight, Operation op);
      83             : 
      84             :   protected:
      85         212 :     static std::string GetName(const std::shared_ptr<GDALMDArray> &arrayLeft,
      86             :                                const std::shared_ptr<GDALMDArray> &arrayRight,
      87             :                                Operation op)
      88             :     {
      89         424 :         return std::string(arrayLeft->GetFullName())
      90         212 :             .append(" ")
      91         212 :             .append(OperationToString(op))
      92         212 :             .append(" ")
      93         424 :             .append(arrayRight->GetFullName());
      94             :     }
      95             : 
      96         106 :     GDALMathOperationMDArray(const std::shared_ptr<GDALMDArray> &arrayLeft,
      97             :                              const std::shared_ptr<GDALMDArray> &arrayRight,
      98             :                              Operation op)
      99         212 :         : GDALAbstractMDArray(std::string(),
     100         106 :                               GetName(arrayLeft, arrayRight, op)),
     101         212 :           GDALPamMDArray(std::string(), GetName(arrayLeft, arrayRight, op),
     102             :                          // Arbitrary linking to arrayLeft for PAM purposes
     103         212 :                          GDALPamMultiDim::GetPAM(arrayLeft),
     104             :                          arrayLeft->GetContext()),
     105             :           m_arrayLeft(arrayLeft), m_arrayRight(arrayRight), m_op(op),
     106             :           m_dt(GDALExtendedDataType::Create(GDT_Float64)),
     107             :           m_dfNoDataLeft(
     108         106 :               m_arrayLeft->GetNoDataValueAsDouble(&m_bHasNoDataLeft)),
     109             :           m_dfNoDataRight(
     110         636 :               m_arrayRight->GetNoDataValueAsDouble(&m_bHasNoDataRight))
     111             :     {
     112         106 :         if (m_bHasNoDataLeft || m_bHasNoDataRight)
     113             :         {
     114          26 :             if (m_bHasNoDataLeft && m_bHasNoDataRight)
     115             :             {
     116          13 :                 if (m_dfNoDataLeft == m_dfNoDataRight)
     117           8 :                     m_dfNoData = m_dfNoDataLeft;
     118             :             }
     119          13 :             else if (m_bHasNoDataLeft)
     120           7 :                 m_dfNoData = m_dfNoDataLeft;
     121             :             else
     122           6 :                 m_dfNoData = m_dfNoDataRight;
     123          26 :             m_abyRawNoDataValue.resize(sizeof(double));
     124          26 :             memcpy(m_abyRawNoDataValue.data(), &m_dfNoData, sizeof(m_dfNoData));
     125             :         }
     126             : 
     127         106 :         const auto &leftUnit = m_arrayLeft->GetUnit();
     128         106 :         const auto &rightUnit = m_arrayRight->GetUnit();
     129         106 :         switch (m_op)
     130             :         {
     131          70 :             case Operation::OP_ADD:
     132             :             case Operation::OP_SUBTRACT:
     133             :             {
     134          70 :                 if (leftUnit == rightUnit)
     135          66 :                     m_osUnit = leftUnit;
     136          70 :                 break;
     137             :             }
     138          36 :             case Operation::OP_MULTIPLY:
     139             :             case Operation::OP_DIVIDE:
     140             :             {
     141          36 :                 if (!leftUnit.empty() && !rightUnit.empty())
     142             :                 {
     143           6 :                     m_osUnit = leftUnit;
     144           6 :                     m_osUnit += ' ';
     145           6 :                     m_osUnit += OperationToString(m_op);
     146           6 :                     m_osUnit += ' ';
     147           6 :                     m_osUnit += rightUnit;
     148             :                 }
     149          36 :                 break;
     150             :             }
     151             :         }
     152         106 :     }
     153             : 
     154           1 :     bool IsWritable() const override
     155             :     {
     156           1 :         return false;
     157             :     }
     158             : 
     159          87 :     const std::string &GetFilename() const override
     160             :     {
     161          87 :         const auto &filename1 = m_arrayLeft->GetFilename();
     162          87 :         const auto &filename2 = m_arrayRight->GetFilename();
     163          87 :         if (filename1 == filename2)
     164          46 :             return filename1;
     165          41 :         static std::string emptyString;
     166          41 :         return emptyString;
     167             :     }
     168             : 
     169             :     const std::vector<std::shared_ptr<GDALDimension>> &
     170         586 :     GetDimensions() const override
     171             :     {
     172         586 :         return m_arrayLeft->GetDimensions();
     173             :     }
     174             : 
     175             :     std::vector<std::shared_ptr<GDALMDArray>>
     176           5 :     GetCoordinateVariables() const override
     177             :     {
     178          10 :         auto left = m_arrayLeft->GetCoordinateVariables();
     179          10 :         auto right = m_arrayRight->GetCoordinateVariables();
     180           5 :         if (left == right)
     181           5 :             return left;
     182           0 :         return GDALMDArray::GetCoordinateVariables();
     183             :     }
     184             : 
     185         294 :     const GDALExtendedDataType &GetDataType() const override
     186             :     {
     187         294 :         return m_dt;
     188             :     }
     189             : 
     190          59 :     const void *GetRawNoDataValue() const override
     191             :     {
     192          59 :         return m_abyRawNoDataValue.empty() ? nullptr
     193          59 :                                            : m_abyRawNoDataValue.data();
     194             :     }
     195             : 
     196          28 :     const std::string &GetUnit() const override
     197             :     {
     198          28 :         return m_osUnit;
     199             :     }
     200             : 
     201           8 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     202             :     {
     203          16 :         auto leftSRS = m_arrayLeft->GetSpatialRef();
     204          16 :         auto rightSRS = m_arrayRight->GetSpatialRef();
     205           8 :         if (!leftSRS || !rightSRS || !leftSRS->IsSame(rightSRS.get()))
     206           7 :             return nullptr;
     207           1 :         return leftSRS;
     208             :     }
     209             : 
     210          20 :     double GetOffset(bool *pbHasOffset,
     211             :                      GDALDataType *peStorageType) const override
     212             :     {
     213          20 :         bool bHasLeftOffset = false;
     214          20 :         GDALDataType leftStorageType = GDT_Unknown;
     215             :         const double dfLeftOffset =
     216          20 :             m_arrayLeft->GetOffset(&bHasLeftOffset, &leftStorageType);
     217             : 
     218          20 :         bool bHasRightOffset = false;
     219          20 :         GDALDataType rightStorageType = GDT_Unknown;
     220             :         const double dfRightOffset =
     221          20 :             m_arrayRight->GetOffset(&bHasRightOffset, &rightStorageType);
     222             : 
     223          20 :         bool bHasLeftScale = false;
     224          20 :         const double dfLeftScale = m_arrayLeft->GetScale(&bHasLeftScale);
     225             : 
     226          20 :         bool bHasRightScale = false;
     227          20 :         const double dfRightScale = m_arrayRight->GetScale(&bHasRightScale);
     228             : 
     229          20 :         bool bRet = false;
     230          20 :         double dfRes = 0.0;
     231             : 
     232          20 :         switch (m_op)
     233             :         {
     234           7 :             case Operation::OP_ADD:
     235             :             {
     236          10 :                 bRet = bHasLeftOffset && bHasRightOffset &&
     237           3 :                        (bHasLeftScale == bHasRightScale &&
     238           3 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     239           7 :                 if (bRet)
     240           2 :                     dfRes = dfLeftOffset + dfRightOffset;
     241           7 :                 break;
     242             :             }
     243             : 
     244           7 :             case Operation::OP_SUBTRACT:
     245             :             {
     246          10 :                 bRet = bHasLeftOffset && bHasRightOffset &&
     247           3 :                        (bHasLeftScale == bHasRightScale &&
     248           3 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     249           7 :                 if (bRet)
     250           2 :                     dfRes = dfLeftOffset - dfRightOffset;
     251           7 :                 break;
     252             :             }
     253             : 
     254           6 :             case Operation::OP_MULTIPLY:
     255             :             case Operation::OP_DIVIDE:
     256           6 :                 break;
     257             :         }
     258             : 
     259          20 :         if (pbHasOffset)
     260          20 :             *pbHasOffset = bRet;
     261          20 :         if (bRet && peStorageType)
     262             :         {
     263           0 :             *peStorageType =
     264           0 :                 GDALDataTypeUnion(leftStorageType, rightStorageType);
     265             :         }
     266             : 
     267          20 :         return dfRes;
     268             :     }
     269             : 
     270           8 :     double GetScale(bool *pbHasScale,
     271             :                     GDALDataType *peStorageType) const override
     272             :     {
     273           8 :         bool bHasLeftOffset = false;
     274           8 :         const double dfLeftOffset = m_arrayLeft->GetOffset(&bHasLeftOffset);
     275             : 
     276           8 :         bool bHasRightOffset = false;
     277           8 :         const double dfRightOffset = m_arrayRight->GetOffset(&bHasRightOffset);
     278             : 
     279           8 :         bool bHasLeftScale = false;
     280           8 :         GDALDataType leftStorageType = GDT_Unknown;
     281             :         const double dfLeftScale =
     282           8 :             m_arrayLeft->GetScale(&bHasLeftScale, &leftStorageType);
     283             : 
     284           8 :         bool bHasRightScale = false;
     285           8 :         GDALDataType rightStorageType = GDT_Unknown;
     286             :         const double dfRightScale =
     287           8 :             m_arrayRight->GetScale(&bHasRightScale, &rightStorageType);
     288             : 
     289           8 :         bool bRet = false;
     290           8 :         double dfRes = 1.0;
     291             : 
     292           8 :         const bool bZeroOffset =
     293          16 :             (bHasLeftOffset == bHasRightOffset &&
     294           8 :              (!bHasLeftOffset ||
     295           4 :               (dfLeftOffset == dfRightOffset && dfLeftOffset == 0)));
     296             : 
     297           8 :         switch (m_op)
     298             :         {
     299           4 :             case Operation::OP_ADD:
     300             :             case Operation::OP_SUBTRACT:
     301             :             {
     302           6 :                 bRet = bZeroOffset &&
     303           2 :                        (bHasLeftScale == bHasRightScale &&
     304           2 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     305           4 :                 if (bRet)
     306           2 :                     dfRes = dfLeftScale;
     307           4 :                 break;
     308             :             }
     309             : 
     310           2 :             case Operation::OP_MULTIPLY:
     311             :             {
     312           2 :                 bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
     313           2 :                 if (bRet)
     314           1 :                     dfRes = dfLeftScale * dfRightScale;
     315           2 :                 break;
     316             :             }
     317             : 
     318           2 :             case Operation::OP_DIVIDE:
     319             :             {
     320           2 :                 bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
     321           2 :                 if (bRet)
     322           1 :                     dfRes = dfLeftScale / dfRightScale;
     323           2 :                 break;
     324             :             }
     325             :         }
     326             : 
     327           8 :         if (pbHasScale)
     328           8 :             *pbHasScale = bRet;
     329           8 :         if (bRet && peStorageType)
     330             :         {
     331           0 :             *peStorageType =
     332           0 :                 GDALDataTypeUnion(leftStorageType, rightStorageType);
     333             :         }
     334             : 
     335           8 :         return dfRes;
     336             :     }
     337             : 
     338          32 :     std::vector<GUInt64> GetBlockSize() const override
     339             :     {
     340          64 :         const auto leftBlockSize = m_arrayLeft->GetBlockSize();
     341          64 :         const auto rightBlockSize = m_arrayRight->GetBlockSize();
     342          32 :         if (leftBlockSize != rightBlockSize)
     343           2 :             return GDALMDArray::GetBlockSize();
     344          30 :         return leftBlockSize;
     345             :     }
     346             : 
     347             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     348             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     349             :                const GDALExtendedDataType &bufferDataType,
     350             :                void *pDstBuffer) const override;
     351             : 
     352             :   private:
     353             :     const std::shared_ptr<GDALMDArray> m_arrayLeft;
     354             :     const std::shared_ptr<GDALMDArray> m_arrayRight;
     355             :     const Operation m_op;
     356             :     const GDALExtendedDataType m_dt;
     357             :     bool m_bHasNoDataLeft = false;
     358             :     bool m_bHasNoDataRight = false;
     359             :     const double m_dfNoDataLeft;
     360             :     const double m_dfNoDataRight;
     361             :     double m_dfNoData = std::numeric_limits<double>::quiet_NaN();
     362             :     std::vector<GByte> m_abyRawNoDataValue{};
     363             :     std::string m_osUnit{};
     364             :     mutable std::vector<double> m_leftValues{};
     365             :     mutable std::vector<double> m_rightValues{};
     366             : 
     367        3891 :     inline bool IsInvalidTuple(double dfLeft, double dfRight) const
     368             :     {
     369        3891 :         return std::isnan(dfLeft) ||
     370        7769 :                (m_bHasNoDataLeft && m_dfNoDataLeft == dfLeft) ||
     371       11660 :                std::isnan(dfRight) ||
     372        7769 :                (m_bHasNoDataRight && m_dfNoDataRight == dfRight);
     373             :     }
     374             : };
     375             : 
     376             : /************************************************************************/
     377             : /*                  GDALMathOperationMDArray::Create()                  */
     378             : /************************************************************************/
     379             : 
     380             : /* static */ std::shared_ptr<GDALMathOperationMDArray>
     381         112 : GDALMathOperationMDArray::Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
     382             :                                  const std::shared_ptr<GDALMDArray> &arrayRight,
     383             :                                  Operation op)
     384             : {
     385         112 :     if (!arrayLeft)
     386             :     {
     387           0 :         CPLError(CE_Failure, CPLE_AppDefined, "arrayLeft is null");
     388           0 :         return nullptr;
     389             :     }
     390             : 
     391         112 :     if (!arrayRight)
     392             :     {
     393           0 :         CPLError(CE_Failure, CPLE_AppDefined, "arrayRight is null");
     394           0 :         return nullptr;
     395             :     }
     396             : 
     397         112 :     if (!arrayLeft->HasSameShapeAs(*arrayRight))
     398             :     {
     399           4 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     400           8 :                  ("Arrays " + arrayLeft->GetFullName() + " and " +
     401          12 :                   arrayRight->GetFullName() + " do not have the same shape")
     402             :                      .c_str());
     403           4 :         return nullptr;
     404             :     }
     405             : 
     406         537 :     for (const auto &array : {arrayLeft, arrayRight})
     407             :     {
     408         215 :         if (array->GetDataType().GetClass() != GEDTC_NUMERIC)
     409             :         {
     410           2 :             CPLError(
     411             :                 CE_Failure, CPLE_AppDefined, "%s",
     412           4 :                 ("Array " + array->GetFullName() + " is not numeric").c_str());
     413           2 :             return nullptr;
     414             :         }
     415             :     }
     416             : 
     417             :     auto newAr(std::shared_ptr<GDALMathOperationMDArray>(
     418         212 :         new GDALMathOperationMDArray(arrayLeft, arrayRight, op)));
     419         106 :     newAr->SetSelf(newAr);
     420         106 :     return newAr;
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                  GDALMathOperationMDArray::IRead()                   */
     425             : /************************************************************************/
     426             : 
     427          86 : bool GDALMathOperationMDArray::IRead(const GUInt64 *arrayStartIdx,
     428             :                                      const size_t *count,
     429             :                                      const GInt64 *arrayStep,
     430             :                                      const GPtrDiff_t *bufferStride,
     431             :                                      const GDALExtendedDataType &bufferDataType,
     432             :                                      void *pDstBuffer) const
     433             : {
     434          86 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
     435             :     {
     436           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     437             :                  "GDALMathOperationMDArray::IRead(): not supported with "
     438             :                  "non-numeric buffer data type");
     439           0 :         return false;
     440             :     }
     441             : 
     442          86 :     const auto &apoDims = GetDimensions();
     443          86 :     const size_t nDims = apoDims.size();
     444             : 
     445             :     const bool bIntegerAndAllValid =
     446          86 :         m_abyRawNoDataValue.empty() &&
     447          63 :         GDALDataTypeIsInteger(
     448         149 :             m_arrayLeft->GetDataType().GetNumericDataType()) &&
     449          29 :         GDALDataTypeIsInteger(m_arrayRight->GetDataType().GetNumericDataType());
     450             : 
     451         104 :     if (bIntegerAndAllValid && m_op == Operation::OP_SUBTRACT &&
     452          18 :         m_arrayLeft == m_arrayRight)
     453             :     {
     454           2 :         CopyContiguousBufferToBuffer(nDims, count, nullptr,
     455           4 :                                      GDALExtendedDataType::Create(GDT_Unknown),
     456             :                                      pDstBuffer, bufferDataType, bufferStride);
     457           2 :         return true;
     458             :     }
     459             : 
     460          84 :     size_t nElts = 1;
     461          84 :     bool bFullArrayRequested = true;
     462         191 :     for (size_t i = 0; i < nDims; ++i)
     463             :     {
     464         107 :         nElts *= count[i];
     465         107 :         if (bFullArrayRequested)
     466         107 :             bFullArrayRequested = (count[i] == apoDims[i]->GetSize());
     467             :     }
     468             :     try
     469             :     {
     470          84 :         if (nElts > m_leftValues.size())
     471          84 :             m_leftValues.resize(nElts);
     472          84 :         if (nElts > m_rightValues.size())
     473          84 :             m_rightValues.resize(nElts);
     474             :     }
     475           0 :     catch (const std::exception &)
     476             :     {
     477           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     478             :                  "GDALMathOperationMDArray::IRead(): out of memory");
     479           0 :         return false;
     480             :     }
     481             : 
     482          84 :     const bool ret = m_arrayLeft->Read(arrayStartIdx, count, arrayStep, nullptr,
     483         168 :                                        m_dt, m_leftValues.data()) &&
     484         168 :                      m_arrayRight->Read(arrayStartIdx, count, arrayStep,
     485          84 :                                         nullptr, m_dt, m_rightValues.data());
     486          84 :     if (ret)
     487             :     {
     488          84 :         switch (m_op)
     489             :         {
     490          10 :             case Operation::OP_ADD:
     491             :             {
     492          10 :                 if (bIntegerAndAllValid)
     493             :                 {
     494          17 :                     for (size_t i = 0; i < nElts; ++i)
     495             :                     {
     496          12 :                         m_leftValues[i] += m_rightValues[i];
     497             :                     }
     498             :                 }
     499             :                 else
     500             :                 {
     501          20 :                     for (size_t i = 0; i < nElts; ++i)
     502             :                     {
     503          15 :                         if (IsInvalidTuple(m_leftValues[i], m_rightValues[i]))
     504           8 :                             m_leftValues[i] = m_dfNoData;
     505             :                         else
     506           7 :                             m_leftValues[i] += m_rightValues[i];
     507             :                     }
     508             :                 }
     509          10 :                 break;
     510             :             }
     511          48 :             case Operation::OP_SUBTRACT:
     512             :             {
     513          48 :                 if (bIntegerAndAllValid)
     514             :                 {
     515        5226 :                     for (size_t i = 0; i < nElts; ++i)
     516             :                     {
     517        5210 :                         m_leftValues[i] -= m_rightValues[i];
     518             :                     }
     519             :                 }
     520             :                 else
     521             :                 {
     522        2124 :                     for (size_t i = 0; i < nElts; ++i)
     523             :                     {
     524        2092 :                         if (IsInvalidTuple(m_leftValues[i], m_rightValues[i]))
     525           6 :                             m_leftValues[i] = m_dfNoData;
     526             :                         else
     527        2086 :                             m_leftValues[i] -= m_rightValues[i];
     528             :                     }
     529             :                 }
     530          48 :                 break;
     531             :             }
     532          19 :             case Operation::OP_MULTIPLY:
     533             :             {
     534          19 :                 if (bIntegerAndAllValid)
     535             :                 {
     536          13 :                     for (size_t i = 0; i < nElts; ++i)
     537             :                     {
     538          10 :                         m_leftValues[i] *= m_rightValues[i];
     539             :                     }
     540             :                 }
     541             :                 else
     542             :                 {
     543        1788 :                     for (size_t i = 0; i < nElts; ++i)
     544             :                     {
     545        1772 :                         if (IsInvalidTuple(m_leftValues[i], m_rightValues[i]))
     546           6 :                             m_leftValues[i] = m_dfNoData;
     547             :                         else
     548        1766 :                             m_leftValues[i] *= m_rightValues[i];
     549             :                     }
     550             :                 }
     551          19 :                 break;
     552             :             }
     553           7 :             case Operation::OP_DIVIDE:
     554             :             {
     555           7 :                 if (bIntegerAndAllValid)
     556             :                 {
     557          13 :                     for (size_t i = 0; i < nElts; ++i)
     558             :                     {
     559          10 :                         m_leftValues[i] /= m_rightValues[i];
     560             :                     }
     561             :                 }
     562             :                 else
     563             :                 {
     564          16 :                     for (size_t i = 0; i < nElts; ++i)
     565             :                     {
     566          12 :                         if (IsInvalidTuple(m_leftValues[i], m_rightValues[i]))
     567           6 :                             m_leftValues[i] = m_dfNoData;
     568             :                         else
     569           6 :                             m_leftValues[i] /= m_rightValues[i];
     570             :                     }
     571             :                 }
     572           7 :                 break;
     573             :             }
     574             :         }
     575             : 
     576          84 :         CopyContiguousBufferToBuffer(nDims, count, m_leftValues.data(), m_dt,
     577             :                                      pDstBuffer, bufferDataType, bufferStride);
     578             :     }
     579             : 
     580          84 :     if (bFullArrayRequested)
     581             :     {
     582          84 :         m_leftValues.clear();
     583          84 :         m_rightValues.clear();
     584             :     }
     585             : 
     586          84 :     return ret;
     587             : }
     588             : 
     589             : //! @endcond
     590             : 
     591             : /************************************************************************/
     592             : /*                             operator+()                              */
     593             : /************************************************************************/
     594             : 
     595             : /** Add this array with another one of the same shape.
     596             :  *
     597             :  * The resulting array is lazy evaluated.
     598             :  *
     599             :  * The resulting array type is Float64.
     600             :  *
     601             :  * The operation is nodata-aware.
     602             :  *
     603             :  * This is the same as C function GDALMDArrayBinaryOperation().
     604             :  *
     605             :  * @since 3.14
     606             :  * @return a new array, or nullptr in case of error
     607             :  */
     608             : std::shared_ptr<GDALMDArray>
     609          40 : GDALMDArray::operator+(const std::shared_ptr<GDALMDArray> &other) const
     610             : {
     611          80 :     return GDALMathOperationMDArray::Create(
     612         120 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_ADD);
     613             : }
     614             : 
     615             : /************************************************************************/
     616             : /*                             operator-()                              */
     617             : /************************************************************************/
     618             : 
     619             : /** Subtract this array with another one of the same shape.
     620             :  *
     621             :  * The resulting array is lazy evaluated.
     622             :  *
     623             :  * The resulting array type is Float64.
     624             :  *
     625             :  * The operation is nodata-aware.
     626             :  *
     627             :  * This is the same as C function GDALMDArrayBinaryOperation().
     628             :  *
     629             :  * @since 3.14
     630             :  * @return a new array, or nullptr in case of error
     631             :  */
     632             : std::shared_ptr<GDALMDArray>
     633          34 : GDALMDArray::operator-(const std::shared_ptr<GDALMDArray> &other) const
     634             : {
     635          68 :     return GDALMathOperationMDArray::Create(
     636         102 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_SUBTRACT);
     637             : }
     638             : 
     639             : /************************************************************************/
     640             : /*                             operator*()                              */
     641             : /************************************************************************/
     642             : 
     643             : /** Multiply this array with another one of the same shape.
     644             :  *
     645             :  * The resulting array is lazy evaluated.
     646             :  *
     647             :  * The resulting array type is Float64.
     648             :  *
     649             :  * The operation is nodata-aware.
     650             :  *
     651             :  * This is the same as C function GDALMDArrayBinaryOperation().
     652             :  *
     653             :  * @since 3.14
     654             :  * @return a new array, or nullptr in case of error
     655             :  */
     656             : std::shared_ptr<GDALMDArray>
     657          22 : GDALMDArray::operator*(const std::shared_ptr<GDALMDArray> &other) const
     658             : {
     659          44 :     return GDALMathOperationMDArray::Create(
     660          66 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_MULTIPLY);
     661             : }
     662             : 
     663             : /************************************************************************/
     664             : /*                             operator/()                              */
     665             : /************************************************************************/
     666             : 
     667             : /** Divide this array by another one of the same shape.
     668             :  *
     669             :  * The resulting array is lazy evaluated.
     670             :  *
     671             :  * The resulting array type is Float64.
     672             :  *
     673             :  * The operation is nodata-aware.
     674             :  *
     675             :  * This is the same as C function GDALMDArrayBinaryOperation().
     676             :  *
     677             :  * @since 3.14
     678             :  * @return a new array, or nullptr in case of error
     679             :  */
     680             : std::shared_ptr<GDALMDArray>
     681          16 : GDALMDArray::operator/(const std::shared_ptr<GDALMDArray> &other) const
     682             : {
     683          32 :     return GDALMathOperationMDArray::Create(
     684          48 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_DIVIDE);
     685             : }

Generated by: LCOV version 1.14