LCOV - code coverage report
Current view: top level - gcore - gdalmultidim_meshgrid.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 111 112 99.1 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Name:     gdalmultiDim_meshgrid.cpp
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Return a vector of coordinate matrices from coordinate vectors.
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_priv.h"
      14             : 
      15             : #include <algorithm>
      16             : #include <limits>
      17             : 
      18             : /************************************************************************/
      19             : /*                       GetConcatenatedNames()                         */
      20             : /************************************************************************/
      21             : 
      22             : static std::string
      23          20 : GetConcatenatedNames(const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays)
      24             : {
      25          20 :     std::string ret;
      26          72 :     for (const auto &poArray : apoArrays)
      27             :     {
      28          52 :         if (!ret.empty())
      29          32 :             ret += ", ";
      30          52 :         ret += poArray->GetFullName();
      31             :     }
      32          20 :     return ret;
      33             : }
      34             : 
      35             : /************************************************************************/
      36             : /*                         GDALMDArrayMeshGrid                          */
      37             : /************************************************************************/
      38             : 
      39             : class GDALMDArrayMeshGrid final : public GDALMDArray
      40             : {
      41             :     const std::vector<std::shared_ptr<GDALMDArray>> m_apoArrays;
      42             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims{};
      43             :     const size_t m_iDim;
      44             :     const bool m_bIJIndexing;
      45             : 
      46             :   protected:
      47          10 :     explicit GDALMDArrayMeshGrid(
      48             :         const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays,
      49             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims, size_t iDim,
      50             :         bool bIJIndexing)
      51          20 :         : GDALAbstractMDArray(std::string(),
      52          20 :                               "Mesh grid view of " +
      53          10 :                                   GetConcatenatedNames(apoArrays)),
      54          20 :           GDALMDArray(std::string(),
      55          20 :                       "Mesh grid view of " + GetConcatenatedNames(apoArrays)),
      56             :           m_apoArrays(apoArrays), m_apoDims(apoDims), m_iDim(iDim),
      57          50 :           m_bIJIndexing(bIJIndexing)
      58             :     {
      59          10 :     }
      60             : 
      61             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      62             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      63             :                const GDALExtendedDataType &bufferDataType,
      64             :                void *pDstBuffer) const override;
      65             : 
      66             :   public:
      67             :     static std::shared_ptr<GDALMDArrayMeshGrid>
      68          10 :     Create(const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays,
      69             :            size_t iDim, bool bIJIndexing)
      70             :     {
      71          20 :         std::vector<std::shared_ptr<GDALDimension>> apoDims;
      72          36 :         for (size_t i = 0; i < apoArrays.size(); ++i)
      73             :         {
      74          26 :             const size_t iTranslatedDim = (!bIJIndexing && i <= 1) ? 1 - i : i;
      75          26 :             apoDims.push_back(apoArrays[iTranslatedDim]->GetDimensions()[0]);
      76             :         }
      77             :         auto newAr(std::shared_ptr<GDALMDArrayMeshGrid>(
      78          10 :             new GDALMDArrayMeshGrid(apoArrays, apoDims, iDim, bIJIndexing)));
      79          10 :         newAr->SetSelf(newAr);
      80          20 :         return newAr;
      81             :     }
      82             : 
      83           1 :     bool IsWritable() const override
      84             :     {
      85           1 :         return false;
      86             :     }
      87             : 
      88          13 :     const std::string &GetFilename() const override
      89             :     {
      90          13 :         return m_apoArrays[m_iDim]->GetFilename();
      91             :     }
      92             : 
      93             :     const std::vector<std::shared_ptr<GDALDimension>> &
      94         165 :     GetDimensions() const override
      95             :     {
      96         165 :         return m_apoDims;
      97             :     }
      98             : 
      99          60 :     const GDALExtendedDataType &GetDataType() const override
     100             :     {
     101          60 :         return m_apoArrays[m_iDim]->GetDataType();
     102             :     }
     103             : 
     104             :     std::shared_ptr<GDALAttribute>
     105           1 :     GetAttribute(const std::string &osName) const override
     106             :     {
     107           1 :         return m_apoArrays[m_iDim]->GetAttribute(osName);
     108             :     }
     109             : 
     110             :     std::vector<std::shared_ptr<GDALAttribute>>
     111           2 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     112             :     {
     113           2 :         return m_apoArrays[m_iDim]->GetAttributes(papszOptions);
     114             :     }
     115             : 
     116           1 :     const std::string &GetUnit() const override
     117             :     {
     118           1 :         return m_apoArrays[m_iDim]->GetUnit();
     119             :     }
     120             : 
     121           1 :     const void *GetRawNoDataValue() const override
     122             :     {
     123           1 :         return m_apoArrays[m_iDim]->GetRawNoDataValue();
     124             :     }
     125             : 
     126           1 :     double GetOffset(bool *pbHasOffset,
     127             :                      GDALDataType *peStorageType) const override
     128             :     {
     129           1 :         return m_apoArrays[m_iDim]->GetOffset(pbHasOffset, peStorageType);
     130             :     }
     131             : 
     132           1 :     double GetScale(bool *pbHasScale,
     133             :                     GDALDataType *peStorageType) const override
     134             :     {
     135           1 :         return m_apoArrays[m_iDim]->GetScale(pbHasScale, peStorageType);
     136             :     }
     137             : };
     138             : 
     139             : /************************************************************************/
     140             : /*                             IRead()                                  */
     141             : /************************************************************************/
     142             : 
     143          20 : bool GDALMDArrayMeshGrid::IRead(const GUInt64 *arrayStartIdx,
     144             :                                 const size_t *count, const GInt64 *arrayStep,
     145             :                                 const GPtrDiff_t *bufferStride,
     146             :                                 const GDALExtendedDataType &bufferDataType,
     147             :                                 void *pDstBuffer) const
     148             : {
     149          20 :     const size_t nBufferDTSize = bufferDataType.GetSize();
     150          20 :     const size_t iTranslatedDim =
     151          20 :         (!m_bIJIndexing && m_iDim <= 1) ? 1 - m_iDim : m_iDim;
     152          40 :     std::vector<GByte> abyTmpData(nBufferDTSize * count[iTranslatedDim]);
     153          20 :     const GPtrDiff_t strideOne[] = {1};
     154          40 :     if (!m_apoArrays[m_iDim]->Read(&arrayStartIdx[iTranslatedDim],
     155          20 :                                    &count[iTranslatedDim],
     156          20 :                                    &arrayStep[iTranslatedDim], strideOne,
     157          20 :                                    bufferDataType, abyTmpData.data()))
     158           0 :         return false;
     159             : 
     160          20 :     const auto nDims = GetDimensionCount();
     161             : 
     162             :     struct Stack
     163             :     {
     164             :         size_t nIters = 0;
     165             :         GByte *dst_ptr = nullptr;
     166             :         GPtrDiff_t dst_inc_offset = 0;
     167             :     };
     168             : 
     169             :     // +1 to avoid -Werror=null-dereference
     170          20 :     std::vector<Stack> stack(nDims + 1);
     171          72 :     for (size_t i = 0; i < nDims; i++)
     172             :     {
     173          52 :         stack[i].dst_inc_offset =
     174          52 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
     175             :     }
     176          20 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
     177          20 :     size_t dimIdx = 0;
     178          20 :     size_t valIdx = 0;
     179             : 
     180         142 : lbl_next_depth:
     181         142 :     if (dimIdx == nDims - 1)
     182             :     {
     183          92 :         auto nIters = count[dimIdx];
     184          92 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
     185          92 :         if (dimIdx == iTranslatedDim)
     186             :         {
     187          34 :             valIdx = 0;
     188             :             while (true)
     189             :             {
     190         120 :                 GDALExtendedDataType::CopyValue(
     191         120 :                     &abyTmpData[nBufferDTSize * valIdx], bufferDataType,
     192             :                     dst_ptr, bufferDataType);
     193         120 :                 if ((--nIters) == 0)
     194          34 :                     break;
     195          86 :                 ++valIdx;
     196          86 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
     197             :             }
     198             :         }
     199             :         else
     200             :         {
     201             :             while (true)
     202             :             {
     203         216 :                 GDALExtendedDataType::CopyValue(
     204         216 :                     &abyTmpData[nBufferDTSize * valIdx], bufferDataType,
     205             :                     dst_ptr, bufferDataType);
     206         216 :                 if ((--nIters) == 0)
     207          58 :                     break;
     208         158 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
     209             :             }
     210             :         }
     211             :     }
     212             :     else
     213             :     {
     214          50 :         if (dimIdx == iTranslatedDim)
     215          18 :             valIdx = 0;
     216          50 :         stack[dimIdx].nIters = count[dimIdx];
     217             :         while (true)
     218             :         {
     219         122 :             dimIdx++;
     220         122 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     221         122 :             goto lbl_next_depth;
     222         122 :         lbl_return_to_caller:
     223         122 :             dimIdx--;
     224         122 :             if ((--stack[dimIdx].nIters) == 0)
     225          50 :                 break;
     226          72 :             if (dimIdx == iTranslatedDim)
     227          26 :                 ++valIdx;
     228          72 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     229             :         }
     230             :     }
     231         142 :     if (dimIdx > 0)
     232             :     {
     233         122 :         goto lbl_return_to_caller;
     234             :     }
     235             : 
     236          20 :     if (bufferDataType.NeedsFreeDynamicMemory())
     237             :     {
     238          20 :         for (size_t i = 0; i < count[iTranslatedDim]; ++i)
     239             :         {
     240          16 :             bufferDataType.FreeDynamicMemory(&abyTmpData[i * nBufferDTSize]);
     241             :         }
     242             :     }
     243             : 
     244          20 :     return true;
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                      GDALMDArrayGetMeshGrid()                        */
     249             : /************************************************************************/
     250             : 
     251             : /** Return a list of multidimensional arrays from a list of one-dimensional
     252             :  * arrays.
     253             :  *
     254             :  * This is typically used to transform one-dimensional longitude, latitude
     255             :  * arrays into 2D ones.
     256             :  *
     257             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
     258             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
     259             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
     260             :  * repeated to fill the matrix along the first dimension for x1, the second
     261             :  * for x2 and so on.
     262             :  *
     263             :  * For example, if x = [1, 2], and y = [3, 4, 5],
     264             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
     265             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
     266             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
     267             :  *
     268             :  * and
     269             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
     270             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
     271             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
     272             :  *
     273             :  * The currently supported options are:
     274             :  * <ul>
     275             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
     276             :  * output.
     277             :  * </li>
     278             :  * </ul>
     279             :  *
     280             :  * This is the same as
     281             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
     282             :  * function.
     283             :  *
     284             :  * This is the same as the C function GDALMDArrayGetMeshGrid()
     285             :  *
     286             :  * @param apoArrays Input arrays
     287             :  * @param papszOptions NULL, or NULL terminated list of options.
     288             :  *
     289             :  * @return an array of coordinate matrices
     290             :  * @since 3.10
     291             :  */
     292             : 
     293           7 : /* static */ std::vector<std::shared_ptr<GDALMDArray>> GDALMDArray::GetMeshGrid(
     294             :     const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays,
     295             :     CSLConstList papszOptions)
     296             : {
     297           7 :     std::vector<std::shared_ptr<GDALMDArray>> ret;
     298          19 :     for (const auto &poArray : apoArrays)
     299             :     {
     300          13 :         if (poArray->GetDimensionCount() != 1)
     301             :         {
     302           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     303             :                      "Only 1-D input arrays are accepted");
     304           1 :             return ret;
     305             :         }
     306             :     }
     307             : 
     308             :     const char *pszIndexing =
     309           6 :         CSLFetchNameValueDef(papszOptions, "INDEXING", "xy");
     310           6 :     if (!EQUAL(pszIndexing, "xy") && !EQUAL(pszIndexing, "ij"))
     311             :     {
     312           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     313             :                  "Only INDEXING=xy or ij is accepted");
     314           1 :         return ret;
     315             :     }
     316           5 :     const bool bIJIndexing = EQUAL(pszIndexing, "ij");
     317             : 
     318          15 :     for (size_t i = 0; i < apoArrays.size(); ++i)
     319             :     {
     320          10 :         ret.push_back(GDALMDArrayMeshGrid::Create(apoArrays, i, bIJIndexing));
     321             :     }
     322             : 
     323           5 :     return ret;
     324             : }

Generated by: LCOV version 1.14