LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_view.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 394 400 98.5 %
Date: 2026-06-19 21:24:00 Functions: 40 40 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_view.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDALMDArray::GetView() implementation
       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 <algorithm>
      19             : #include <limits>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : /************************************************************************/
      24             : /*                          GDALSlicedMDArray                           */
      25             : /************************************************************************/
      26             : 
      27             : class GDALSlicedMDArray final : public GDALPamMDArray
      28             : {
      29             :   private:
      30             :     std::shared_ptr<GDALMDArray> m_poParent{};
      31             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
      32             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
      33             :     std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
      34             :     std::vector<Range>
      35             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
      36             : 
      37             :     mutable std::vector<GUInt64> m_parentStart;
      38             :     mutable std::vector<size_t> m_parentCount;
      39             :     mutable std::vector<GInt64> m_parentStep;
      40             :     mutable std::vector<GPtrDiff_t> m_parentStride;
      41             : 
      42             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
      43             :                              const GInt64 *arrayStep,
      44             :                              const GPtrDiff_t *bufferStride) const;
      45             : 
      46             :   protected:
      47         862 :     explicit GDALSlicedMDArray(
      48             :         const std::shared_ptr<GDALMDArray> &poParent,
      49             :         const std::string &viewExpr,
      50             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
      51             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
      52             :         std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
      53             :         std::vector<Range> &&parentRanges)
      54        2586 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
      55        2586 :                                                  poParent->GetFullName() +
      56        1724 :                                                  " (" + viewExpr + ")"),
      57        1724 :           GDALPamMDArray(std::string(),
      58        1724 :                          "Sliced view of " + poParent->GetFullName() + " (" +
      59        1724 :                              viewExpr + ")",
      60        1724 :                          GDALPamMultiDim::GetPAM(poParent),
      61             :                          poParent->GetContext()),
      62        1724 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
      63         862 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
      64         862 :           m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
      65         862 :           m_parentRanges(std::move(parentRanges)),
      66         862 :           m_parentStart(m_poParent->GetDimensionCount()),
      67         862 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
      68         862 :           m_parentStep(m_poParent->GetDimensionCount()),
      69        6896 :           m_parentStride(m_poParent->GetDimensionCount())
      70             :     {
      71         862 :     }
      72             : 
      73             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      74             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      75             :                const GDALExtendedDataType &bufferDataType,
      76             :                void *pDstBuffer) const override;
      77             : 
      78             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      79             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      80             :                 const GDALExtendedDataType &bufferDataType,
      81             :                 const void *pSrcBuffer) override;
      82             : 
      83             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      84             :                      CSLConstList papszOptions) const override;
      85             : 
      86             :   public:
      87             :     static std::shared_ptr<GDALSlicedMDArray>
      88         862 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
      89             :            const std::string &viewExpr,
      90             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
      91             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
      92             :            std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
      93             :            std::vector<Range> &&parentRanges)
      94             :     {
      95         862 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
      96         862 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
      97             : 
      98             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
      99         862 :             poParent, viewExpr, std::move(dims),
     100         862 :             std::move(mapDimIdxToParentDimIdx),
     101         862 :             std::move(apoNewIndexingVariables), std::move(parentRanges))));
     102         862 :         newAr->SetSelf(newAr);
     103         862 :         return newAr;
     104             :     }
     105             : 
     106         228 :     bool IsWritable() const override
     107             :     {
     108         228 :         return m_poParent->IsWritable();
     109             :     }
     110             : 
     111        1614 :     const std::string &GetFilename() const override
     112             :     {
     113        1614 :         return m_poParent->GetFilename();
     114             :     }
     115             : 
     116             :     const std::vector<std::shared_ptr<GDALDimension>> &
     117        6217 :     GetDimensions() const override
     118             :     {
     119        6217 :         return m_dims;
     120             :     }
     121             : 
     122        2445 :     const GDALExtendedDataType &GetDataType() const override
     123             :     {
     124        2445 :         return m_poParent->GetDataType();
     125             :     }
     126             : 
     127           4 :     const std::string &GetUnit() const override
     128             :     {
     129           4 :         return m_poParent->GetUnit();
     130             :     }
     131             : 
     132             :     // bool SetUnit(const std::string& osUnit) override  { return
     133             :     // m_poParent->SetUnit(osUnit); }
     134             : 
     135           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     136             :     {
     137           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
     138           2 :         if (!poSrcSRS)
     139           1 :             return nullptr;
     140           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
     141           2 :         std::vector<int> dstMapping;
     142           3 :         for (int srcAxis : srcMapping)
     143             :         {
     144           2 :             bool bFound = false;
     145           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
     146             :             {
     147           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
     148           3 :                     srcAxis - 1)
     149             :                 {
     150           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
     151           2 :                     bFound = true;
     152           2 :                     break;
     153             :                 }
     154             :             }
     155           2 :             if (!bFound)
     156             :             {
     157           0 :                 dstMapping.push_back(0);
     158             :             }
     159             :         }
     160           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
     161           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
     162           1 :         return poClone;
     163             :     }
     164             : 
     165         104 :     const void *GetRawNoDataValue() const override
     166             :     {
     167         104 :         return m_poParent->GetRawNoDataValue();
     168             :     }
     169             : 
     170             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
     171             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
     172             : 
     173           4 :     double GetOffset(bool *pbHasOffset,
     174             :                      GDALDataType *peStorageType) const override
     175             :     {
     176           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
     177             :     }
     178             : 
     179           4 :     double GetScale(bool *pbHasScale,
     180             :                     GDALDataType *peStorageType) const override
     181             :     {
     182           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
     183             :     }
     184             : 
     185             :     // bool SetOffset(double dfOffset) override { return
     186             :     // m_poParent->SetOffset(dfOffset); }
     187             : 
     188             :     // bool SetScale(double dfScale) override { return
     189             :     // m_poParent->SetScale(dfScale); }
     190             : 
     191         580 :     std::vector<GUInt64> GetBlockSize() const override
     192             :     {
     193         580 :         std::vector<GUInt64> ret(GetDimensionCount());
     194        1160 :         const auto parentBlockSize(m_poParent->GetBlockSize());
     195        1564 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
     196             :         {
     197         984 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
     198         984 :             if (iOldAxis != static_cast<size_t>(-1))
     199             :             {
     200         984 :                 ret[i] = parentBlockSize[iOldAxis];
     201             :             }
     202             :         }
     203        1160 :         return ret;
     204             :     }
     205             : 
     206             :     std::shared_ptr<GDALAttribute>
     207          14 :     GetAttribute(const std::string &osName) const override
     208             :     {
     209          14 :         return m_poParent->GetAttribute(osName);
     210             :     }
     211             : 
     212             :     std::vector<std::shared_ptr<GDALAttribute>>
     213          37 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     214             :     {
     215          37 :         return m_poParent->GetAttributes(papszOptions);
     216             :     }
     217             : };
     218             : 
     219             : /************************************************************************/
     220             : /*                        PrepareParentArrays()                         */
     221             : /************************************************************************/
     222             : 
     223         763 : void GDALSlicedMDArray::PrepareParentArrays(
     224             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
     225             :     const GPtrDiff_t *bufferStride) const
     226             : {
     227         763 :     const size_t nParentDimCount = m_parentRanges.size();
     228        2148 :     for (size_t i = 0; i < nParentDimCount; i++)
     229             :     {
     230             :         // For dimensions in parent that have no existence in sliced array
     231        1385 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
     232             :     }
     233             : 
     234        1919 :     for (size_t i = 0; i < m_dims.size(); i++)
     235             :     {
     236        1156 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
     237        1156 :         if (iParent != static_cast<size_t>(-1))
     238             :         {
     239        1154 :             m_parentStart[iParent] =
     240        1154 :                 m_parentRanges[iParent].m_nIncr >= 0
     241        1154 :                     ? m_parentRanges[iParent].m_nStartIdx +
     242         852 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
     243         302 :                     : m_parentRanges[iParent].m_nStartIdx -
     244         604 :                           arrayStartIdx[i] *
     245         302 :                               static_cast<GUInt64>(
     246         302 :                                   -m_parentRanges[iParent].m_nIncr);
     247        1154 :             m_parentCount[iParent] = count[i];
     248        1154 :             if (arrayStep)
     249             :             {
     250        1151 :                 m_parentStep[iParent] =
     251        1151 :                     count[i] == 1 ? 1 :
     252             :                                   // other checks should have ensured this does
     253             :                         // not overflow
     254         947 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
     255             :             }
     256        1154 :             if (bufferStride)
     257             :             {
     258        1151 :                 m_parentStride[iParent] = bufferStride[i];
     259             :             }
     260             :         }
     261             :     }
     262         763 : }
     263             : 
     264             : /************************************************************************/
     265             : /*                               IRead()                                */
     266             : /************************************************************************/
     267             : 
     268         732 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     269             :                               const GInt64 *arrayStep,
     270             :                               const GPtrDiff_t *bufferStride,
     271             :                               const GDALExtendedDataType &bufferDataType,
     272             :                               void *pDstBuffer) const
     273             : {
     274         732 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
     275        1464 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
     276         732 :                             m_parentStep.data(), m_parentStride.data(),
     277         732 :                             bufferDataType, pDstBuffer);
     278             : }
     279             : 
     280             : /************************************************************************/
     281             : /*                               IWrite()                               */
     282             : /************************************************************************/
     283             : 
     284          29 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
     285             :                                const size_t *count, const GInt64 *arrayStep,
     286             :                                const GPtrDiff_t *bufferStride,
     287             :                                const GDALExtendedDataType &bufferDataType,
     288             :                                const void *pSrcBuffer)
     289             : {
     290          29 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
     291          58 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
     292          29 :                              m_parentStep.data(), m_parentStride.data(),
     293          29 :                              bufferDataType, pSrcBuffer);
     294             : }
     295             : 
     296             : /************************************************************************/
     297             : /*                            IAdviseRead()                             */
     298             : /************************************************************************/
     299             : 
     300           2 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
     301             :                                     const size_t *count,
     302             :                                     CSLConstList papszOptions) const
     303             : {
     304           2 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
     305           2 :     const size_t nDims = GetDimensionCount();
     306           5 :     for (size_t i = 0; i < nDims; ++i)
     307             :     {
     308           3 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
     309           3 :         if (iParent != static_cast<size_t>(-1))
     310             :         {
     311           3 :             if (m_parentRanges[iParent].m_nIncr < 0)
     312             :             {
     313           2 :                 m_parentStart[iParent] -= (-m_parentRanges[iParent].m_nIncr) *
     314           1 :                                               m_parentCount[iParent] -
     315             :                                           1;
     316             :             }
     317           3 :             m_parentCount[iParent] = static_cast<size_t>(std::min<uint64_t>(
     318             :                 {static_cast<uint64_t>(std::numeric_limits<size_t>::max()),
     319           3 :                  static_cast<uint64_t>(
     320           3 :                      m_parentCount[iParent] *
     321           3 :                      std::abs(m_parentRanges[iParent].m_nIncr)),
     322           3 :                  static_cast<uint64_t>(
     323           6 :                      m_poParent->GetDimensions()[iParent]->GetSize() -
     324           3 :                      m_parentStart[iParent])}));
     325             :         }
     326             :     }
     327           2 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
     328           2 :                                   papszOptions);
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                         CreateSlicedArray()                          */
     333             : /************************************************************************/
     334             : 
     335             : static std::shared_ptr<GDALMDArray>
     336         724 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
     337             :                   const std::string &viewExpr, const std::string &activeSlice,
     338             :                   bool bRenameDimensions,
     339             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
     340             : {
     341         724 :     const auto &srcDims(self->GetDimensions());
     342         724 :     if (srcDims.empty())
     343             :     {
     344           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
     345           2 :         return nullptr;
     346             :     }
     347             : 
     348        1444 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
     349         722 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
     350             : 
     351        1444 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
     352        1444 :     std::vector<size_t> mapDimIdxToParentDimIdx;
     353        1444 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
     354         722 :     newDims.reserve(nTokens);
     355         722 :     mapDimIdxToParentDimIdx.reserve(nTokens);
     356         722 :     parentRanges.reserve(nTokens);
     357             : 
     358         722 :     bool bGotEllipsis = false;
     359         722 :     size_t nCurSrcDim = 0;
     360        1444 :     std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
     361        2151 :     for (size_t i = 0; i < nTokens; i++)
     362             :     {
     363        1446 :         const char *pszIdxSpec = aosTokens[i];
     364        1446 :         if (EQUAL(pszIdxSpec, "..."))
     365             :         {
     366         129 :             if (bGotEllipsis)
     367             :             {
     368           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     369             :                          "Only one single ellipsis is supported");
     370           2 :                 return nullptr;
     371             :             }
     372         127 :             bGotEllipsis = true;
     373         127 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
     374         263 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
     375             :             {
     376         136 :                 parentRanges.emplace_back(0, 1);
     377         136 :                 newDims.push_back(srcDims[nCurSrcDim]);
     378         136 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
     379             :             }
     380         127 :             continue;
     381             :         }
     382        1317 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
     383        1314 :                  EQUAL(pszIdxSpec, "np.newaxis"))
     384             :         {
     385           3 :             newDims.push_back(std::make_shared<GDALDimension>(
     386           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
     387           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
     388           3 :             continue;
     389             :         }
     390        1314 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
     391             :         {
     392         342 :             if (nCurSrcDim >= srcDims.size())
     393             :             {
     394           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
     395             :                          activeSlice.c_str());
     396           7 :                 return nullptr;
     397             :             }
     398             : 
     399         340 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
     400         340 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
     401         340 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
     402         336 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
     403             :             {
     404           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
     405             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
     406           5 :                 return nullptr;
     407             :             }
     408         335 :             if (nVal < 0)
     409           0 :                 nVal += nDimSize;
     410         335 :             parentRanges.emplace_back(nVal, 0);
     411             :         }
     412             :         else
     413             :         {
     414         972 :             if (nCurSrcDim >= srcDims.size())
     415             :             {
     416           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
     417             :                          activeSlice.c_str());
     418           8 :                 return nullptr;
     419             :             }
     420             : 
     421             :             CPLStringList aosRangeTokens(
     422         971 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
     423         971 :             int nRangeTokens = aosRangeTokens.size();
     424         971 :             if (nRangeTokens > 3)
     425             :             {
     426           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
     427             :                          pszIdxSpec);
     428           1 :                 return nullptr;
     429             :             }
     430         970 :             if (nRangeTokens <= 1)
     431             :             {
     432           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
     433             :                          pszIdxSpec);
     434           1 :                 return nullptr;
     435             :             }
     436         969 :             const char *pszStart = aosRangeTokens[0];
     437         969 :             const char *pszEnd = aosRangeTokens[1];
     438         969 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
     439         969 :             GDALSlicedMDArray::Range range;
     440         969 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
     441         969 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
     442        1937 :             if (range.m_nIncr == 0 ||
     443         968 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
     444             :             {
     445           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
     446           1 :                 return nullptr;
     447             :             }
     448         968 :             auto startIdx(CPLAtoGIntBig(pszStart));
     449         968 :             if (startIdx < 0)
     450             :             {
     451           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
     452           0 :                     startIdx = 0;
     453             :                 else
     454           0 :                     startIdx = nDimSize + startIdx;
     455             :             }
     456         968 :             const bool bPosIncr = range.m_nIncr > 0;
     457         968 :             range.m_nStartIdx = startIdx;
     458        1936 :             range.m_nStartIdx = EQUAL(pszStart, "")
     459         968 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
     460             :                                     : range.m_nStartIdx;
     461         968 :             if (range.m_nStartIdx >= nDimSize - 1)
     462         286 :                 range.m_nStartIdx = nDimSize - 1;
     463         968 :             auto endIdx(CPLAtoGIntBig(pszEnd));
     464         968 :             if (endIdx < 0)
     465             :             {
     466           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
     467           1 :                 if (nDimSize < positiveEndIdx)
     468           0 :                     endIdx = 0;
     469             :                 else
     470           1 :                     endIdx = nDimSize - positiveEndIdx;
     471             :             }
     472         968 :             GUInt64 nEndIdx = endIdx;
     473         968 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
     474         968 :             if (pszStart[0] || pszEnd[0])
     475             :             {
     476         636 :                 if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
     477         633 :                     (!bPosIncr && range.m_nStartIdx <= nEndIdx))
     478             :                 {
     479           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
     480             :                              "Output dimension of size 0 is not allowed");
     481           4 :                     return nullptr;
     482             :                 }
     483             :             }
     484         964 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
     485         964 :             const auto nAbsIncr = std::abs(range.m_nIncr);
     486             :             const GUInt64 newSize =
     487         332 :                 (pszStart[0] == 0 && pszEnd[0] == 0 &&
     488         332 :                  range.m_nStartIdx == nEndIdx)
     489        1928 :                     ? 1
     490             :                 : bPosIncr
     491         963 :                     ? cpl::div_round_up(nEndIdx - range.m_nStartIdx, nAbsIncr)
     492         128 :                     : cpl::div_round_up(inc + range.m_nStartIdx - nEndIdx,
     493         964 :                                         nAbsIncr);
     494         964 :             const auto &poSrcDim = srcDims[nCurSrcDim];
     495        1544 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
     496         580 :                 newSize == poSrcDim->GetSize())
     497             :             {
     498         211 :                 newDims.push_back(poSrcDim);
     499             :             }
     500             :             else
     501             :             {
     502        1506 :                 std::string osNewDimName(poSrcDim->GetName());
     503         753 :                 if (bRenameDimensions)
     504             :                 {
     505             :                     osNewDimName =
     506        1410 :                         "subset_" + poSrcDim->GetName() +
     507             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
     508             :                                    "_" CPL_FRMT_GUIB,
     509         705 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
     510         705 :                                    static_cast<GIntBig>(range.m_nIncr),
     511         705 :                                    static_cast<GUIntBig>(newSize));
     512             :                 }
     513             :                 auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     514        1506 :                     std::string(), osNewDimName, poSrcDim->GetType(),
     515         753 :                     range.m_nIncr > 0 ? poSrcDim->GetDirection()
     516             :                                       : std::string(),
     517        1506 :                     newSize);
     518         753 :                 auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
     519         910 :                 if (poSrcIndexingVar &&
     520         910 :                     poSrcIndexingVar->GetDimensionCount() == 1 &&
     521         157 :                     poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
     522             :                 {
     523             :                     std::vector<std::shared_ptr<GDALDimension>>
     524         628 :                         indexingVarNewDims{poNewDim};
     525         314 :                     std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
     526             :                     std::vector<std::shared_ptr<GDALMDArray>>
     527         314 :                         indexingVarNewIndexingVar;
     528             :                     std::vector<GDALSlicedMDArray::Range>
     529         314 :                         indexingVarParentRanges{range};
     530             :                     auto poNewIndexingVar = GDALSlicedMDArray::Create(
     531             :                         poSrcIndexingVar, pszIdxSpec,
     532         157 :                         std::move(indexingVarNewDims),
     533         157 :                         std::move(indexingVarMapDimIdxToParentDimIdx),
     534         157 :                         std::move(indexingVarNewIndexingVar),
     535         471 :                         std::move(indexingVarParentRanges));
     536         157 :                     poNewDim->SetIndexingVariable(poNewIndexingVar);
     537         157 :                     apoNewIndexingVariables.push_back(
     538         157 :                         std::move(poNewIndexingVar));
     539             :                 }
     540         753 :                 newDims.push_back(std::move(poNewDim));
     541             :             }
     542         964 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
     543         964 :             parentRanges.emplace_back(range);
     544             :         }
     545             : 
     546        1299 :         nCurSrcDim++;
     547             :     }
     548         778 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
     549             :     {
     550          73 :         parentRanges.emplace_back(0, 1);
     551          73 :         newDims.push_back(srcDims[nCurSrcDim]);
     552          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
     553             :     }
     554             : 
     555         705 :     GDALMDArray::ViewSpec viewSpec;
     556         705 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
     557         705 :     viewSpec.m_parentRanges = parentRanges;
     558         705 :     viewSpecs.emplace_back(std::move(viewSpec));
     559             : 
     560        1410 :     return GDALSlicedMDArray::Create(
     561         705 :         self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
     562        1410 :         std::move(apoNewIndexingVariables), std::move(parentRanges));
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                       GDALExtractFieldMDArray                        */
     567             : /************************************************************************/
     568             : 
     569             : class GDALExtractFieldMDArray final : public GDALPamMDArray
     570             : {
     571             :   private:
     572             :     std::shared_ptr<GDALMDArray> m_poParent{};
     573             :     GDALExtendedDataType m_dt;
     574             :     std::string m_srcCompName;
     575             :     mutable std::vector<GByte> m_pabyNoData{};
     576             : 
     577             :   protected:
     578         465 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
     579             :                             const std::string &fieldName,
     580             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
     581        1860 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
     582         930 :                                                  " of " +
     583         465 :                                                  poParent->GetFullName()),
     584             :           GDALPamMDArray(
     585         930 :               std::string(),
     586         930 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
     587         930 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
     588             :           m_poParent(poParent), m_dt(srcComp->GetType()),
     589        2325 :           m_srcCompName(srcComp->GetName())
     590             :     {
     591         465 :         m_pabyNoData.resize(m_dt.GetSize());
     592         465 :     }
     593             : 
     594             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     595             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     596             :                const GDALExtendedDataType &bufferDataType,
     597             :                void *pDstBuffer) const override;
     598             : 
     599           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
     600             :                      CSLConstList papszOptions) const override
     601             :     {
     602           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
     603             :     }
     604             : 
     605             :   public:
     606             :     static std::shared_ptr<GDALExtractFieldMDArray>
     607         465 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
     608             :            const std::string &fieldName,
     609             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
     610             :     {
     611             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
     612         465 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
     613         465 :         newAr->SetSelf(newAr);
     614         465 :         return newAr;
     615             :     }
     616             : 
     617         930 :     ~GDALExtractFieldMDArray() override
     618         465 :     {
     619         465 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
     620         930 :     }
     621             : 
     622         200 :     bool IsWritable() const override
     623             :     {
     624         200 :         return m_poParent->IsWritable();
     625             :     }
     626             : 
     627        1116 :     const std::string &GetFilename() const override
     628             :     {
     629        1116 :         return m_poParent->GetFilename();
     630             :     }
     631             : 
     632             :     const std::vector<std::shared_ptr<GDALDimension>> &
     633        1583 :     GetDimensions() const override
     634             :     {
     635        1583 :         return m_poParent->GetDimensions();
     636             :     }
     637             : 
     638        1137 :     const GDALExtendedDataType &GetDataType() const override
     639             :     {
     640        1137 :         return m_dt;
     641             :     }
     642             : 
     643           2 :     const std::string &GetUnit() const override
     644             :     {
     645           2 :         return m_poParent->GetUnit();
     646             :     }
     647             : 
     648           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     649             :     {
     650           2 :         return m_poParent->GetSpatialRef();
     651             :     }
     652             : 
     653          99 :     const void *GetRawNoDataValue() const override
     654             :     {
     655          99 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
     656          99 :         if (parentNoData == nullptr)
     657           6 :             return nullptr;
     658             : 
     659          93 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
     660          93 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
     661             : 
     662         186 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
     663         186 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
     664         186 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
     665          93 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
     666         279 :                                                 std::move(comps)));
     667             : 
     668          93 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
     669          93 :                                         &m_pabyNoData[0], tmpDT);
     670             : 
     671          93 :         return &m_pabyNoData[0];
     672             :     }
     673             : 
     674           2 :     double GetOffset(bool *pbHasOffset,
     675             :                      GDALDataType *peStorageType) const override
     676             :     {
     677           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
     678             :     }
     679             : 
     680           2 :     double GetScale(bool *pbHasScale,
     681             :                     GDALDataType *peStorageType) const override
     682             :     {
     683           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
     684             :     }
     685             : 
     686         201 :     std::vector<GUInt64> GetBlockSize() const override
     687             :     {
     688         201 :         return m_poParent->GetBlockSize();
     689             :     }
     690             : };
     691             : 
     692             : /************************************************************************/
     693             : /*                               IRead()                                */
     694             : /************************************************************************/
     695             : 
     696         414 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
     697             :                                     const size_t *count,
     698             :                                     const GInt64 *arrayStep,
     699             :                                     const GPtrDiff_t *bufferStride,
     700             :                                     const GDALExtendedDataType &bufferDataType,
     701             :                                     void *pDstBuffer) const
     702             : {
     703         828 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
     704         828 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
     705         828 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
     706             :     auto tmpDT(GDALExtendedDataType::Create(
     707         828 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
     708             : 
     709         414 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
     710         828 :                             tmpDT, pDstBuffer);
     711             : }
     712             : 
     713             : /************************************************************************/
     714             : /*                    CreateFieldNameExtractArray()                     */
     715             : /************************************************************************/
     716             : 
     717             : static std::shared_ptr<GDALMDArray>
     718         466 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
     719             :                             const std::string &fieldName)
     720             : {
     721         466 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
     722         466 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
     723         983 :     for (const auto &comp : self->GetDataType().GetComponents())
     724             :     {
     725         982 :         if (comp->GetName() == fieldName)
     726             :         {
     727         465 :             srcComp = &comp;
     728         465 :             break;
     729             :         }
     730             :     }
     731         466 :     if (srcComp == nullptr)
     732             :     {
     733           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
     734             :                  fieldName.c_str());
     735           1 :         return nullptr;
     736             :     }
     737         465 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
     738             : }
     739             : 
     740             : //! @endcond
     741             : 
     742             : /************************************************************************/
     743             : /*                              GetView()                               */
     744             : /************************************************************************/
     745             : 
     746             : // clang-format off
     747             : /** Return a view of the array using slicing or field access.
     748             :  *
     749             :  * The slice expression uses the same syntax as NumPy basic slicing and
     750             :  * indexing. See
     751             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
     752             :  * Or it can use field access by name. See
     753             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
     754             :  *
     755             :  * Multiple [] bracket elements can be concatenated, with a slice expression
     756             :  * or field name inside each.
     757             :  *
     758             :  * For basic slicing and indexing, inside each [] bracket element, a list of
     759             :  * indexes that apply to successive source dimensions, can be specified, using
     760             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
     761             :  * or newaxis, using a comma separator.
     762             :  *
     763             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
     764             :  * <ul>
     765             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
     766             :  *     at index 1 in the first dimension, and index 2 in the second dimension
     767             :  *     from the source array. That is 5</li>
     768             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
     769             :  * implemented internally doing this intermediate slicing approach.</li>
     770             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
     771             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
     772             :  *     first dimension. That is [4,5,6,7].</li>
     773             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
     774             :  *     second dimension. That is [2,6].</li>
     775             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
     776             :  * the second dimension. That is [[2],[6]].</li>
     777             :  * <li>GetView("[::,2]"): Same as
     778             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
     779             :  * ellipsis only expands to one dimension here.</li>
     780             :  * <li>GetView("[:,::2]"):
     781             :  * returns a 2-dimensional array, with even-indexed elements of the second
     782             :  * dimension. That is [[0,2],[4,6]].</li>
     783             :  * <li>GetView("[:,1::2]"): returns a
     784             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
     785             :  * is [[1,3],[5,7]].</li>
     786             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
     787             :  * array, with elements of the second dimension with index in the range [1,3[.
     788             :  * That is [[1,2],[5,6]].</li>
     789             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
     790             :  * array, with the values in first dimension reversed. That is
     791             :  * [[4,5,6,7],[0,1,2,3]].</li>
     792             :  * <li>GetView("[newaxis,...]"): returns a
     793             :  * 3-dimensional array, with an additional dimension of size 1 put at the
     794             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
     795             :  * </ul>
     796             :  *
     797             :  * One difference with NumPy behavior is that ranges that would result in
     798             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
     799             :  * GDAL multidimensional model).
     800             :  *
     801             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
     802             :  * Multiple field specification is not supported currently.
     803             :  *
     804             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
     805             :  *
     806             :  * \note When using the GDAL Python bindings, natural Python syntax can be
     807             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
     808             :  * ar.GetView("[0,::,1]['foo']")
     809             :  * \note When using the C++ API and integer indexing only, you may use the
     810             :  * at(idx0, idx1, ...) method.
     811             :  *
     812             :  * The returned array holds a reference to the original one, and thus is
     813             :  * a view of it (not a copy). If the content of the original array changes,
     814             :  * the content of the view array too. When using basic slicing and indexing,
     815             :  * the view can be written if the underlying array is writable.
     816             :  *
     817             :  * This is the same as the C function GDALMDArrayGetView()
     818             :  *
     819             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
     820             :  * access.
     821             :  * @return a new array, that holds a reference to the original one, and thus is
     822             :  * a view of it (not a copy), or nullptr in case of error.
     823             :  */
     824             : // clang-format on
     825             : 
     826             : std::shared_ptr<GDALMDArray>
     827        1124 : GDALMDArray::GetView(const std::string &viewExpr) const
     828             : {
     829        2248 :     std::vector<ViewSpec> viewSpecs;
     830        2248 :     return GetView(viewExpr, true, viewSpecs);
     831             : }
     832             : 
     833             : //! @cond Doxygen_Suppress
     834             : std::shared_ptr<GDALMDArray>
     835        1196 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
     836             :                      std::vector<ViewSpec> &viewSpecs) const
     837             : {
     838        2392 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
     839        1196 :     if (!self)
     840             :     {
     841           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     842             :                  "Driver implementation issue: m_pSelf not set !");
     843           1 :         return nullptr;
     844             :     }
     845        1195 :     std::string curExpr(viewExpr);
     846             :     while (true)
     847             :     {
     848        1198 :         if (curExpr.empty() || curExpr[0] != '[')
     849             :         {
     850           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     851             :                      "Slice string should start with ['");
     852        1195 :             return nullptr;
     853             :         }
     854             : 
     855        1196 :         std::string fieldName;
     856             :         size_t endExpr;
     857        1196 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
     858             :         {
     859         470 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
     860             :             {
     861           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     862             :                          "Field access not allowed on non-compound data type");
     863           2 :                 return nullptr;
     864             :             }
     865         468 :             size_t idx = 2;
     866        5119 :             for (; idx < curExpr.size(); idx++)
     867             :             {
     868        5118 :                 const char ch = curExpr[idx];
     869        5118 :                 if (ch == curExpr[1])
     870         467 :                     break;
     871        4651 :                 if (ch == '\\' && idx + 1 < curExpr.size())
     872             :                 {
     873           1 :                     fieldName += curExpr[idx + 1];
     874           1 :                     idx++;
     875             :                 }
     876             :                 else
     877             :                 {
     878        4650 :                     fieldName += ch;
     879             :                 }
     880             :             }
     881         468 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
     882             :             {
     883           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     884             :                          "Invalid field access specification");
     885           2 :                 return nullptr;
     886             :             }
     887         466 :             endExpr = idx + 1;
     888             :         }
     889             :         else
     890             :         {
     891         726 :             endExpr = curExpr.find(']');
     892             :         }
     893        1192 :         if (endExpr == std::string::npos)
     894             :         {
     895           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
     896           1 :             return nullptr;
     897             :         }
     898        1191 :         if (endExpr == 1)
     899             :         {
     900           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
     901           1 :             return nullptr;
     902             :         }
     903        1190 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
     904             : 
     905        1190 :         if (!fieldName.empty())
     906             :         {
     907         932 :             ViewSpec viewSpec;
     908         466 :             viewSpec.m_osFieldName = fieldName;
     909         466 :             viewSpecs.emplace_back(std::move(viewSpec));
     910             :         }
     911             : 
     912        1190 :         auto newArray = !fieldName.empty()
     913             :                             ? CreateFieldNameExtractArray(self, fieldName)
     914             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
     915        1190 :                                                 bRenameDimensions, viewSpecs);
     916             : 
     917        1190 :         if (endExpr == curExpr.size() - 1)
     918             :         {
     919        1187 :             return newArray;
     920             :         }
     921           3 :         self = std::move(newArray);
     922           3 :         curExpr = curExpr.substr(endExpr + 1);
     923           3 :     }
     924             : }
     925             : 
     926             : //! @endcond
     927             : 
     928             : std::shared_ptr<GDALMDArray>
     929          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
     930             : {
     931          19 :     std::string osExpr("[");
     932          19 :     bool bFirst = true;
     933          45 :     for (const auto &idx : indices)
     934             :     {
     935          26 :         if (!bFirst)
     936           7 :             osExpr += ',';
     937          26 :         bFirst = false;
     938          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
     939             :     }
     940          57 :     return GetView(osExpr + ']');
     941             : }
     942             : 
     943             : /************************************************************************/
     944             : /*                              operator[]                              */
     945             : /************************************************************************/
     946             : 
     947             : /** Return a view of the array using field access
     948             :  *
     949             :  * Equivalent of GetView("['fieldName']")
     950             :  *
     951             :  * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
     952             :  */
     953             : std::shared_ptr<GDALMDArray>
     954           2 : GDALMDArray::operator[](const std::string &fieldName) const
     955             : {
     956           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
     957           4 :                                             .replaceAll('\\', "\\\\")
     958           4 :                                             .replaceAll('\'', "\\\'")
     959           6 :                                             .c_str()));
     960             : }

Generated by: LCOV version 1.14