LCOV - code coverage report
Current view: top level - gcore - gdalmultidim_gridded.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 314 364 86.3 %
Date: 2025-01-18 12:42:00 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_gridded.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDALMDArray::GetGridded() implementation
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_alg.h"
      15             : #include "gdalgrid.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_pam.h"
      18             : #include "ogrsf_frmts.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <cassert>
      22             : #include <limits>
      23             : #include <new>
      24             : 
      25             : /************************************************************************/
      26             : /*                         GDALMDArrayGridded                           */
      27             : /************************************************************************/
      28             : 
      29             : class GDALMDArrayGridded final : public GDALPamMDArray
      30             : {
      31             :   private:
      32             :     std::shared_ptr<GDALMDArray> m_poParent{};
      33             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims{};
      34             :     std::shared_ptr<GDALMDArray> m_poVarX{};
      35             :     std::shared_ptr<GDALMDArray> m_poVarY{};
      36             :     std::unique_ptr<GDALDataset> m_poVectorDS{};
      37             :     GDALGridAlgorithm m_eAlg;
      38             :     std::unique_ptr<void, VSIFreeReleaser> m_poGridOptions;
      39             :     const GDALExtendedDataType m_dt;
      40             :     std::vector<GUInt64> m_anBlockSize{};
      41             :     const double m_dfNoDataValue;
      42             :     const double m_dfMinX;
      43             :     const double m_dfResX;
      44             :     const double m_dfMinY;
      45             :     const double m_dfResY;
      46             :     const double m_dfRadius;
      47             :     mutable std::vector<GUInt64> m_anLastStartIdx{};
      48             :     mutable std::vector<double> m_adfZ{};
      49             : 
      50             :   protected:
      51           4 :     explicit GDALMDArrayGridded(
      52             :         const std::shared_ptr<GDALMDArray> &poParent,
      53             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
      54             :         const std::shared_ptr<GDALMDArray> &poVarX,
      55             :         const std::shared_ptr<GDALMDArray> &poVarY,
      56             :         std::unique_ptr<GDALDataset> &&poVectorDS, GDALGridAlgorithm eAlg,
      57             :         std::unique_ptr<void, VSIFreeReleaser> &&poGridOptions,
      58             :         double dfNoDataValue, double dfMinX, double dfResX, double dfMinY,
      59             :         double dfResY, double dfRadius)
      60           8 :         : GDALAbstractMDArray(std::string(),
      61           8 :                               "Gridded view of " + poParent->GetFullName()),
      62             :           GDALPamMDArray(
      63           8 :               std::string(), "Gridded view of " + poParent->GetFullName(),
      64           8 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      65           4 :           m_poParent(std::move(poParent)), m_apoDims(apoDims), m_poVarX(poVarX),
      66           4 :           m_poVarY(poVarY), m_poVectorDS(std::move(poVectorDS)), m_eAlg(eAlg),
      67           4 :           m_poGridOptions(std::move(poGridOptions)),
      68             :           m_dt(GDALExtendedDataType::Create(GDT_Float64)),
      69             :           m_dfNoDataValue(dfNoDataValue), m_dfMinX(dfMinX), m_dfResX(dfResX),
      70          20 :           m_dfMinY(dfMinY), m_dfResY(dfResY), m_dfRadius(dfRadius)
      71             :     {
      72           4 :         const auto anParentBlockSize = m_poParent->GetBlockSize();
      73           4 :         m_anBlockSize.resize(m_apoDims.size());
      74          12 :         for (size_t i = 0; i + 1 < m_apoDims.size(); ++i)
      75           8 :             m_anBlockSize[i] = anParentBlockSize[i];
      76           4 :         m_anBlockSize[m_apoDims.size() - 2] = 256;
      77           4 :         m_anBlockSize[m_apoDims.size() - 1] = 256;
      78           4 :     }
      79             : 
      80             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      81             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      82             :                const GDALExtendedDataType &bufferDataType,
      83             :                void *pDstBuffer) const override;
      84             : 
      85             :   public:
      86             :     static std::shared_ptr<GDALMDArrayGridded>
      87           4 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
      88             :            const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
      89             :            const std::shared_ptr<GDALMDArray> &poVarX,
      90             :            const std::shared_ptr<GDALMDArray> &poVarY,
      91             :            std::unique_ptr<GDALDataset> &&poVectorDS, GDALGridAlgorithm eAlg,
      92             :            std::unique_ptr<void, VSIFreeReleaser> &&poGridOptions,
      93             :            double dfNoDataValue, double dfMinX, double dfResX, double dfMinY,
      94             :            double dfResY, double dfRadius)
      95             :     {
      96             :         auto newAr(std::shared_ptr<GDALMDArrayGridded>(new GDALMDArrayGridded(
      97           4 :             poParent, apoDims, poVarX, poVarY, std::move(poVectorDS), eAlg,
      98           4 :             std::move(poGridOptions), dfNoDataValue, dfMinX, dfResX, dfMinY,
      99           4 :             dfResY, dfRadius)));
     100           4 :         newAr->SetSelf(newAr);
     101           4 :         return newAr;
     102             :     }
     103             : 
     104           1 :     bool IsWritable() const override
     105             :     {
     106           1 :         return false;
     107             :     }
     108             : 
     109           9 :     const std::string &GetFilename() const override
     110             :     {
     111           9 :         return m_poParent->GetFilename();
     112             :     }
     113             : 
     114             :     const std::vector<std::shared_ptr<GDALDimension>> &
     115          63 :     GetDimensions() const override
     116             :     {
     117          63 :         return m_apoDims;
     118             :     }
     119             : 
     120           1 :     const void *GetRawNoDataValue() const override
     121             :     {
     122           1 :         return &m_dfNoDataValue;
     123             :     }
     124             : 
     125           2 :     std::vector<GUInt64> GetBlockSize() const override
     126             :     {
     127           2 :         return m_anBlockSize;
     128             :     }
     129             : 
     130          25 :     const GDALExtendedDataType &GetDataType() const override
     131             :     {
     132          25 :         return m_dt;
     133             :     }
     134             : 
     135           1 :     const std::string &GetUnit() const override
     136             :     {
     137           1 :         return m_poParent->GetUnit();
     138             :     }
     139             : 
     140           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     141             :     {
     142           1 :         return m_poParent->GetSpatialRef();
     143             :     }
     144             : 
     145             :     std::shared_ptr<GDALAttribute>
     146           1 :     GetAttribute(const std::string &osName) const override
     147             :     {
     148           1 :         return m_poParent->GetAttribute(osName);
     149             :     }
     150             : 
     151             :     std::vector<std::shared_ptr<GDALAttribute>>
     152           2 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     153             :     {
     154           2 :         return m_poParent->GetAttributes(papszOptions);
     155             :     }
     156             : };
     157             : 
     158             : /************************************************************************/
     159             : /*                             IRead()                                  */
     160             : /************************************************************************/
     161             : 
     162           7 : bool GDALMDArrayGridded::IRead(const GUInt64 *arrayStartIdx,
     163             :                                const size_t *count, const GInt64 *arrayStep,
     164             :                                const GPtrDiff_t *bufferStride,
     165             :                                const GDALExtendedDataType &bufferDataType,
     166             :                                void *pDstBuffer) const
     167             : {
     168           7 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
     169             :     {
     170           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     171             :                  "GDALMDArrayGridded::IRead() only support numeric "
     172             :                  "bufferDataType");
     173           0 :         return false;
     174             :     }
     175           7 :     const auto nDims = GetDimensionCount();
     176             : 
     177          14 :     std::vector<GUInt64> anStartIdx;
     178          13 :     for (size_t i = 0; i + 2 < nDims; ++i)
     179             :     {
     180           7 :         anStartIdx.push_back(arrayStartIdx[i]);
     181           7 :         if (count[i] != 1)
     182             :         {
     183           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     184             :                      "GDALMDArrayGridded::IRead() only support count = 1 in "
     185             :                      "the first dimensions, except the last 2 Y,X ones");
     186           1 :             return false;
     187             :         }
     188             :     }
     189             : 
     190           6 :     const auto iDimX = nDims - 1;
     191           6 :     const auto iDimY = nDims - 2;
     192             : 
     193           6 :     if (arrayStep[iDimX] < 0)
     194             :     {
     195           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     196             :                  "GDALMDArrayGridded::IRead(): "
     197             :                  "arrayStep[iDimX] < 0 not supported");
     198           1 :         return false;
     199             :     }
     200             : 
     201           5 :     if (arrayStep[iDimY] < 0)
     202             :     {
     203           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     204             :                  "GDALMDArrayGridded::IRead(): "
     205             :                  "arrayStep[iDimY] < 0 not supported");
     206           0 :         return false;
     207             :     }
     208             : 
     209             :     // Load the values taken by the variable at the considered slice
     210             :     // (if not already done)
     211           5 :     if (m_adfZ.empty() || m_anLastStartIdx != anStartIdx)
     212             :     {
     213           4 :         std::vector<GUInt64> anTempStartIdx(anStartIdx);
     214           4 :         anTempStartIdx.push_back(0);
     215             :         const std::vector<GInt64> anTempArrayStep(
     216           4 :             m_poParent->GetDimensionCount(), 1);
     217             :         std::vector<GPtrDiff_t> anTempBufferStride(
     218           4 :             m_poParent->GetDimensionCount() - 1, 0);
     219           4 :         anTempBufferStride.push_back(1);
     220           4 :         std::vector<size_t> anTempCount(m_poParent->GetDimensionCount() - 1, 1);
     221           4 :         anTempCount.push_back(
     222           4 :             static_cast<size_t>(m_poParent->GetDimensions().back()->GetSize()));
     223           4 :         CPLAssert(anTempStartIdx.size() == m_poParent->GetDimensionCount());
     224           4 :         CPLAssert(anTempCount.size() == m_poParent->GetDimensionCount());
     225           4 :         CPLAssert(anTempArrayStep.size() == m_poParent->GetDimensionCount());
     226           4 :         CPLAssert(anTempBufferStride.size() == m_poParent->GetDimensionCount());
     227             : 
     228             :         try
     229             :         {
     230           4 :             m_adfZ.resize(anTempCount.back());
     231             :         }
     232           0 :         catch (const std::bad_alloc &e)
     233             :         {
     234           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     235           0 :             return false;
     236             :         }
     237             : 
     238           8 :         if (!m_poParent->Read(anTempStartIdx.data(), anTempCount.data(),
     239           4 :                               anTempArrayStep.data(), anTempBufferStride.data(),
     240           4 :                               m_dt, m_adfZ.data()))
     241             :         {
     242           0 :             return false;
     243             :         }
     244             :         // GCC 13.1 warns here. Definitely a false positive.
     245             : #if defined(__GNUC__) && __GNUC__ >= 13
     246             : #pragma GCC diagnostic push
     247             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     248             : #endif
     249           4 :         m_anLastStartIdx = std::move(anStartIdx);
     250             : #if defined(__GNUC__) && __GNUC__ >= 13
     251             : #pragma GCC diagnostic pop
     252             : #endif
     253             :     }
     254             : 
     255             :     // Determine the X,Y spatial extent of the request
     256           5 :     const double dfX1 = m_dfMinX + arrayStartIdx[iDimX] * m_dfResX;
     257           5 :     const double dfX2 = m_dfMinX + (arrayStartIdx[iDimX] +
     258           5 :                                     (count[iDimX] - 1) * arrayStep[iDimX]) *
     259           5 :                                        m_dfResX;
     260           5 :     const double dfMinX = std::min(dfX1, dfX2) - m_dfResX / 2;
     261           5 :     const double dfMaxX = std::max(dfX1, dfX2) + m_dfResX / 2;
     262             : 
     263           5 :     const double dfY1 = m_dfMinY + arrayStartIdx[iDimY] * m_dfResY;
     264           5 :     const double dfY2 = m_dfMinY + (arrayStartIdx[iDimY] +
     265           5 :                                     (count[iDimY] - 1) * arrayStep[iDimY]) *
     266           5 :                                        m_dfResY;
     267           5 :     const double dfMinY = std::min(dfY1, dfY2) - m_dfResY / 2;
     268           5 :     const double dfMaxY = std::max(dfY1, dfY2) + m_dfResY / 2;
     269             : 
     270             :     // Extract relevant variable values
     271           5 :     auto poLyr = m_poVectorDS->GetLayer(0);
     272           9 :     if (!(arrayStartIdx[iDimX] == 0 &&
     273           4 :           arrayStartIdx[iDimX] + (count[iDimX] - 1) * arrayStep[iDimX] ==
     274           4 :               m_apoDims[iDimX]->GetSize() - 1 &&
     275           4 :           arrayStartIdx[iDimY] == 0 &&
     276           4 :           arrayStartIdx[iDimY] + (count[iDimY] - 1) * arrayStep[iDimY] ==
     277           4 :               m_apoDims[iDimY]->GetSize() - 1))
     278             :     {
     279           1 :         poLyr->SetSpatialFilterRect(dfMinX - m_dfRadius, dfMinY - m_dfRadius,
     280           1 :                                     dfMaxX + m_dfRadius, dfMaxY + m_dfRadius);
     281             :     }
     282             :     else
     283             :     {
     284           4 :         poLyr->SetSpatialFilter(nullptr);
     285             :     }
     286          10 :     std::vector<double> adfX;
     287          10 :     std::vector<double> adfY;
     288          10 :     std::vector<double> adfZ;
     289             :     try
     290             :     {
     291          35 :         for (auto &&poFeat : poLyr)
     292             :         {
     293          30 :             const auto poGeom = poFeat->GetGeometryRef();
     294          30 :             CPLAssert(poGeom);
     295          30 :             CPLAssert(poGeom->getGeometryType() == wkbPoint);
     296          30 :             adfX.push_back(poGeom->toPoint()->getX());
     297          30 :             adfY.push_back(poGeom->toPoint()->getY());
     298          30 :             const auto nIdxInDataset = poFeat->GetFieldAsInteger64(0);
     299          30 :             assert(nIdxInDataset >= 0 &&
     300             :                    static_cast<size_t>(nIdxInDataset) < m_adfZ.size());
     301          30 :             adfZ.push_back(m_adfZ[static_cast<size_t>(nIdxInDataset)]);
     302             :         }
     303             :     }
     304           0 :     catch (const std::bad_alloc &e)
     305             :     {
     306           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     307           0 :         return false;
     308             :     }
     309             : 
     310           5 :     const size_t nXSize =
     311           5 :         static_cast<size_t>((count[iDimX] - 1) * arrayStep[iDimX] + 1);
     312           5 :     const size_t nYSize =
     313           5 :         static_cast<size_t>((count[iDimY] - 1) * arrayStep[iDimY] + 1);
     314           5 :     if (nXSize > std::numeric_limits<GUInt32>::max() / nYSize)
     315             :     {
     316           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     317             :                  "Too many points queried at once");
     318           0 :         return false;
     319             :     }
     320          10 :     std::vector<double> adfRes;
     321             :     try
     322             :     {
     323           5 :         adfRes.resize(nXSize * nYSize, m_dfNoDataValue);
     324             :     }
     325           0 :     catch (const std::bad_alloc &e)
     326             :     {
     327           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     328           0 :         return false;
     329             :     }
     330             : #if 0
     331             :     CPLDebug("GDAL",
     332             :              "dfMinX=%f, dfMaxX=%f, dfMinY=%f, dfMaxY=%f",
     333             :              dfMinX, dfMaxX, dfMinY, dfMaxY);
     334             : #endif
     335             : 
     336             :     // Finally do the gridded interpolation
     337          10 :     if (!adfX.empty() &&
     338           5 :         GDALGridCreate(
     339           5 :             m_eAlg, m_poGridOptions.get(), static_cast<GUInt32>(adfX.size()),
     340           5 :             adfX.data(), adfY.data(), adfZ.data(), dfMinX, dfMaxX, dfMinY,
     341             :             dfMaxY, static_cast<GUInt32>(nXSize), static_cast<GUInt32>(nYSize),
     342           5 :             GDT_Float64, adfRes.data(), nullptr, nullptr) != CE_None)
     343             :     {
     344           0 :         return false;
     345             :     }
     346             : 
     347             :     // Copy interpolated data into destination buffer
     348           5 :     GByte *pabyDestBuffer = static_cast<GByte *>(pDstBuffer);
     349           5 :     const auto eBufferDT = bufferDataType.GetNumericDataType();
     350           5 :     const auto nBufferDTSize = GDALGetDataTypeSizeBytes(eBufferDT);
     351          15 :     for (size_t iY = 0; iY < count[iDimY]; ++iY)
     352             :     {
     353          20 :         GDALCopyWords64(
     354          10 :             adfRes.data() + iY * arrayStep[iDimY] * nXSize, GDT_Float64,
     355          10 :             static_cast<int>(sizeof(double) * arrayStep[iDimX]),
     356          10 :             pabyDestBuffer + iY * bufferStride[iDimY] * nBufferDTSize,
     357          10 :             eBufferDT, static_cast<int>(bufferStride[iDimX] * nBufferDTSize),
     358          10 :             static_cast<GPtrDiff_t>(count[iDimX]));
     359             :     }
     360             : 
     361           5 :     return true;
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                            GetGridded()                              */
     366             : /************************************************************************/
     367             : 
     368             : /** Return a gridded array from scattered point data, that is from an array
     369             :  * whose last dimension is the indexing variable of X and Y arrays.
     370             :  *
     371             :  * The gridding is done in 2D, using GDALGridCreate(), on-the-fly at Read()
     372             :  * time, taking into account the spatial extent of the request to limit the
     373             :  * gridding. The results got on the whole extent or a subset of it might not be
     374             :  * strictly identical depending on the gridding algorithm and its radius.
     375             :  * Setting a radius in osGridOptions is recommended to improve performance.
     376             :  * For arrays which have more dimensions than the dimension of the indexing
     377             :  * variable of the X and Y arrays, Read() must be called on slices of the extra
     378             :  * dimensions (ie count[i] must be set to 1, except for the X and Y dimensions
     379             :  * of the array returned by this method).
     380             :  *
     381             :  * This is the same as the C function GDALMDArrayGetGridded().
     382             :  *
     383             :  * @param osGridOptions Gridding algorithm and options.
     384             :  * e.g. "invdist:nodata=nan:radius1=1:radius2=1:max_points=5".
     385             :  * See documentation of the <a href="/programs/gdal_grid.html">gdal_grid</a>
     386             :  * utility for all options.
     387             :  * @param poXArrayIn Single-dimension array containing X values, and whose
     388             :  * dimension is the last one of this array. If set to nullptr, the "coordinates"
     389             :  * attribute must exist on this array, and the X variable will be the (N-1)th one
     390             :  * mentioned in it, unless there is a "x" or "lon" variable in "coordinates".
     391             :  * @param poYArrayIn Single-dimension array containing Y values, and whose
     392             :  * dimension is the last one of this array. If set to nullptr, the "coordinates"
     393             :  * attribute must exist on this array, and the Y variable will be the (N-2)th one
     394             :  * mentioned in it,  unless there is a "y" or "lat" variable in "coordinates".
     395             :  * @param papszOptions NULL terminated list of options, or nullptr. Supported
     396             :  * options are:
     397             :  * <ul>
     398             :  * <li>RESOLUTION=val: Spatial resolution of the returned array. If not set,
     399             :  * will be guessed from the typical spacing of (X,Y) points.</li>
     400             :  * </ul>
     401             :  *
     402             :  * @return gridded array, or nullptr in case of error.
     403             :  *
     404             :  * @since GDAL 3.7
     405             :  */
     406             : std::shared_ptr<GDALMDArray>
     407          23 : GDALMDArray::GetGridded(const std::string &osGridOptions,
     408             :                         const std::shared_ptr<GDALMDArray> &poXArrayIn,
     409             :                         const std::shared_ptr<GDALMDArray> &poYArrayIn,
     410             :                         CSLConstList papszOptions) const
     411             : {
     412          46 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
     413          23 :     if (!self)
     414             :     {
     415           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     416             :                  "Driver implementation issue: m_pSelf not set !");
     417           0 :         return nullptr;
     418             :     }
     419             : 
     420             :     GDALGridAlgorithm eAlg;
     421          23 :     void *pOptions = nullptr;
     422          23 :     if (GDALGridParseAlgorithmAndOptions(osGridOptions.c_str(), &eAlg,
     423          23 :                                          &pOptions) != CE_None)
     424             :     {
     425           1 :         return nullptr;
     426             :     }
     427             : 
     428          44 :     std::unique_ptr<void, VSIFreeReleaser> poGridOptions(pOptions);
     429             : 
     430          22 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
     431             :     {
     432           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     433             :                  "GetDataType().GetClass() != GEDTC_NUMERIC");
     434           1 :         return nullptr;
     435             :     }
     436             : 
     437          21 :     if (GetDimensionCount() == 0)
     438             :     {
     439           1 :         CPLError(CE_Failure, CPLE_NotSupported, "GetDimensionCount() == 0");
     440           1 :         return nullptr;
     441             :     }
     442             : 
     443          20 :     if (poXArrayIn && !poYArrayIn)
     444             :     {
     445           1 :         CPLError(
     446             :             CE_Failure, CPLE_AppDefined,
     447             :             "As poXArrayIn is specified, poYArrayIn must also be specified");
     448           1 :         return nullptr;
     449             :     }
     450          19 :     else if (!poXArrayIn && poYArrayIn)
     451             :     {
     452           1 :         CPLError(
     453             :             CE_Failure, CPLE_AppDefined,
     454             :             "As poYArrayIn is specified, poXArrayIn must also be specified");
     455           1 :         return nullptr;
     456             :     }
     457          36 :     std::shared_ptr<GDALMDArray> poXArray = poXArrayIn;
     458          36 :     std::shared_ptr<GDALMDArray> poYArray = poYArrayIn;
     459             : 
     460          18 :     if (!poXArray)
     461             :     {
     462           8 :         const auto aoCoordVariables = GetCoordinateVariables();
     463           8 :         if (aoCoordVariables.size() < 2)
     464             :         {
     465           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     466             :                      "aoCoordVariables.size() < 2");
     467           1 :             return nullptr;
     468             :         }
     469             : 
     470           7 :         if (aoCoordVariables.size() != GetDimensionCount() + 1)
     471             :         {
     472           2 :             CPLError(CE_Failure, CPLE_NotSupported,
     473             :                      "aoCoordVariables.size() != GetDimensionCount() + 1");
     474           2 :             return nullptr;
     475             :         }
     476             : 
     477             :         // Default choice for X and Y arrays
     478           5 :         poYArray = aoCoordVariables[aoCoordVariables.size() - 2];
     479           5 :         poXArray = aoCoordVariables[aoCoordVariables.size() - 1];
     480             : 
     481             :         // Detect X and Y array from coordinate variables
     482          20 :         for (const auto &poVar : aoCoordVariables)
     483             :         {
     484          15 :             const auto &osVarName = poVar->GetName();
     485             : #ifdef disabled
     486             :             if (poVar->GetDimensionCount() != 1)
     487             :             {
     488             :                 CPLError(CE_Failure, CPLE_NotSupported,
     489             :                          "Coordinate variable %s is not 1-dimensional",
     490             :                          osVarName.c_str());
     491             :                 return nullptr;
     492             :             }
     493             :             const auto &osVarDimName = poVar->GetDimensions()[0]->GetFullName();
     494             :             bool bFound = false;
     495             :             for (const auto &poDim : GetDimensions())
     496             :             {
     497             :                 if (osVarDimName == poDim->GetFullName())
     498             :                 {
     499             :                     bFound = true;
     500             :                     break;
     501             :                 }
     502             :             }
     503             :             if (!bFound)
     504             :             {
     505             :                 CPLError(CE_Failure, CPLE_NotSupported,
     506             :                          "Dimension %s of coordinate variable %s is not a "
     507             :                          "dimension of this array",
     508             :                          osVarDimName.c_str(), osVarName.c_str());
     509             :                 return nullptr;
     510             :             }
     511             : #endif
     512          15 :             if (osVarName == "x" || osVarName == "lon")
     513             :             {
     514           0 :                 poXArray = poVar;
     515             :             }
     516          15 :             else if (osVarName == "y" || osVarName == "lat")
     517             :             {
     518           0 :                 poYArray = poVar;
     519             :             }
     520             :         }
     521             :     }
     522             : 
     523          15 :     if (poYArray->GetDimensionCount() != 1)
     524             :     {
     525           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     526             :                  "aoCoordVariables[aoCoordVariables.size() - "
     527             :                  "2]->GetDimensionCount() != 1");
     528           1 :         return nullptr;
     529             :     }
     530          14 :     if (poYArray->GetDataType().GetClass() != GEDTC_NUMERIC)
     531             :     {
     532           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     533             :                  "poYArray->GetDataType().GetClass() != GEDTC_NUMERIC");
     534           1 :         return nullptr;
     535             :     }
     536             : 
     537          13 :     if (poXArray->GetDimensionCount() != 1)
     538             :     {
     539           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     540             :                  "aoCoordVariables[aoCoordVariables.size() - "
     541             :                  "1]->GetDimensionCount() != 1");
     542           1 :         return nullptr;
     543             :     }
     544          12 :     if (poXArray->GetDataType().GetClass() != GEDTC_NUMERIC)
     545             :     {
     546           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     547             :                  "poXArray->GetDataType().GetClass() != GEDTC_NUMERIC");
     548           1 :         return nullptr;
     549             :     }
     550             : 
     551          11 :     if (poYArray->GetDimensions()[0]->GetFullName() !=
     552          11 :         poXArray->GetDimensions()[0]->GetFullName())
     553             :     {
     554           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     555             :                  "poYArray->GetDimensions()[0]->GetFullName() != "
     556             :                  "poXArray->GetDimensions()[0]->GetFullName()");
     557           3 :         return nullptr;
     558             :     }
     559             : 
     560           8 :     if (poXArray->GetDimensions()[0]->GetFullName() !=
     561           8 :         GetDimensions().back()->GetFullName())
     562             :     {
     563           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     564             :                  "poYArray->GetDimensions()[0]->GetFullName() != "
     565             :                  "GetDimensions().back()->GetFullName()");
     566           0 :         return nullptr;
     567             :     }
     568             : 
     569           8 :     if (poXArray->GetTotalElementsCount() <= 2)
     570             :     {
     571           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     572             :                  "poXArray->GetTotalElementsCount() <= 2");
     573           1 :         return nullptr;
     574             :     }
     575             : 
     576           7 :     if (poXArray->GetTotalElementsCount() >
     577           7 :         std::numeric_limits<size_t>::max() / 2)
     578             :     {
     579           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     580             :                  "poXArray->GetTotalElementsCount() > "
     581             :                  "std::numeric_limits<size_t>::max() / 2");
     582           0 :         return nullptr;
     583             :     }
     584             : 
     585           8 :     if (poXArray->GetTotalElementsCount() > 10 * 1024 * 1024 &&
     586           1 :         !CPLTestBool(CSLFetchNameValueDef(
     587             :             papszOptions, "ACCEPT_BIG_SPATIAL_INDEXING_VARIABLE", "NO")))
     588             :     {
     589           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     590             :                  "The spatial indexing variable has " CPL_FRMT_GIB " elements. "
     591             :                  "Set the ACCEPT_BIG_SPATIAL_INDEXING_VARIABLE=YES option of "
     592             :                  "GetGridded() to mean you want to continue and are aware of "
     593             :                  "big RAM and CPU time requirements",
     594           1 :                  static_cast<GIntBig>(poXArray->GetTotalElementsCount()));
     595           1 :         return nullptr;
     596             :     }
     597             : 
     598          12 :     std::vector<double> adfXVals;
     599          12 :     std::vector<double> adfYVals;
     600             :     try
     601             :     {
     602           6 :         adfXVals.resize(static_cast<size_t>(poXArray->GetTotalElementsCount()));
     603           6 :         adfYVals.resize(static_cast<size_t>(poXArray->GetTotalElementsCount()));
     604             :     }
     605           0 :     catch (const std::bad_alloc &e)
     606             :     {
     607           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     608           0 :         return nullptr;
     609             :     }
     610             : 
     611             :     // Ingest X and Y arrays
     612           6 :     const GUInt64 arrayStartIdx[] = {0};
     613           6 :     const size_t count[] = {adfXVals.size()};
     614           6 :     const GInt64 arrayStep[] = {1};
     615           6 :     const GPtrDiff_t bufferStride[] = {1};
     616          12 :     if (!poXArray->Read(arrayStartIdx, count, arrayStep, bufferStride,
     617          12 :                         GDALExtendedDataType::Create(GDT_Float64),
     618           6 :                         adfXVals.data()))
     619             :     {
     620           0 :         CPLError(CE_Failure, CPLE_AppDefined, "poXArray->Read() failed");
     621           0 :         return nullptr;
     622             :     }
     623          12 :     if (!poYArray->Read(arrayStartIdx, count, arrayStep, bufferStride,
     624          12 :                         GDALExtendedDataType::Create(GDT_Float64),
     625           6 :                         adfYVals.data()))
     626             :     {
     627           0 :         CPLError(CE_Failure, CPLE_AppDefined, "poYArray->Read() failed");
     628           0 :         return nullptr;
     629             :     }
     630             : 
     631           6 :     const char *pszExt = "fgb";
     632           6 :     GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName("FlatGeoBuf");
     633           6 :     if (!poDrv)
     634             :     {
     635           0 :         pszExt = "gpkg";
     636           0 :         poDrv = GetGDALDriverManager()->GetDriverByName("GPKG");
     637           0 :         if (!poDrv)
     638             :         {
     639           0 :             pszExt = "mem";
     640           0 :             poDrv = GetGDALDriverManager()->GetDriverByName("Memory");
     641           0 :             if (!poDrv)
     642             :             {
     643           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     644             :                          "Cannot get driver FlatGeoBuf, GPKG or Memory");
     645           0 :                 return nullptr;
     646             :             }
     647             :         }
     648             :     }
     649             : 
     650             :     // Create a in-memory vector layer with (X,Y) points
     651             :     const std::string osTmpFilename(VSIMemGenerateHiddenFilename(
     652          18 :         std::string("tmp.").append(pszExt).c_str()));
     653             :     auto poDS = std::unique_ptr<GDALDataset>(
     654          12 :         poDrv->Create(osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
     655           6 :     if (!poDS)
     656           0 :         return nullptr;
     657           6 :     auto poLyr = poDS->CreateLayer("layer", nullptr, wkbPoint);
     658           6 :     if (!poLyr)
     659           0 :         return nullptr;
     660          12 :     OGRFieldDefn oFieldDefn("IDX", OFTInteger64);
     661           6 :     poLyr->CreateField(&oFieldDefn);
     662           6 :     if (poLyr->StartTransaction() != OGRERR_NONE)
     663           0 :         return nullptr;
     664          12 :     OGRFeature oFeat(poLyr->GetLayerDefn());
     665          42 :     for (size_t i = 0; i < adfXVals.size(); ++i)
     666             :     {
     667          36 :         auto poPoint = new OGRPoint(adfXVals[i], adfYVals[i]);
     668          36 :         oFeat.SetFID(OGRNullFID);
     669          36 :         oFeat.SetGeometryDirectly(poPoint);
     670          36 :         oFeat.SetField(0, static_cast<GIntBig>(i));
     671          36 :         if (poLyr->CreateFeature(&oFeat) != OGRERR_NONE)
     672           0 :             return nullptr;
     673             :     }
     674           6 :     if (poLyr->CommitTransaction() != OGRERR_NONE)
     675           0 :         return nullptr;
     676           6 :     OGREnvelope sEnvelope;
     677           6 :     CPL_IGNORE_RET_VAL(poLyr->GetExtent(&sEnvelope));
     678           6 :     if (!EQUAL(pszExt, "mem"))
     679             :     {
     680           6 :         if (poDS->Close() != OGRERR_NONE)
     681           0 :             return nullptr;
     682           6 :         poDS.reset(GDALDataset::Open(osTmpFilename.c_str(), GDAL_OF_VECTOR));
     683           6 :         if (!poDS)
     684           0 :             return nullptr;
     685           6 :         poDS->MarkSuppressOnClose();
     686             :     }
     687             : 
     688             :     /* Set of constraints:
     689             :     nX * nY = nCount
     690             :     nX * res = MaxX - MinX
     691             :     nY * res = MaxY - MinY
     692             :     */
     693             : 
     694             :     double dfRes;
     695           6 :     const char *pszRes = CSLFetchNameValue(papszOptions, "RESOLUTION");
     696           6 :     if (pszRes)
     697             :     {
     698           3 :         dfRes = CPLAtofM(pszRes);
     699             :     }
     700             :     else
     701             :     {
     702           3 :         const double dfTotalArea = (sEnvelope.MaxY - sEnvelope.MinY) *
     703           3 :                                    (sEnvelope.MaxX - sEnvelope.MinX);
     704           3 :         dfRes = sqrt(dfTotalArea / static_cast<double>(adfXVals.size()));
     705             :         // CPLDebug("GDAL", "dfRes = %f", dfRes);
     706             : 
     707             :         // Take 10 "random" points in the set, and find the minimum distance from
     708             :         // each to the closest one. And take the geometric average of those minimum
     709             :         // distances as the resolution.
     710           3 :         const size_t nNumSamplePoints = std::min<size_t>(10, adfXVals.size());
     711             : 
     712           3 :         poLyr = poDS->GetLayer(0);
     713           3 :         double dfSumDist2Min = 0;
     714           3 :         int nCountDistMin = 0;
     715          21 :         for (size_t i = 0; i < nNumSamplePoints; ++i)
     716             :         {
     717          18 :             const auto nIdx = i * adfXVals.size() / nNumSamplePoints;
     718          90 :             poLyr->SetSpatialFilterRect(
     719          18 :                 adfXVals[nIdx] - 2 * dfRes, adfYVals[nIdx] - 2 * dfRes,
     720          18 :                 adfXVals[nIdx] + 2 * dfRes, adfYVals[nIdx] + 2 * dfRes);
     721          18 :             double dfDist2Min = std::numeric_limits<double>::max();
     722         102 :             for (auto &&poFeat : poLyr)
     723             :             {
     724          84 :                 const auto poGeom = poFeat->GetGeometryRef();
     725          84 :                 CPLAssert(poGeom);
     726          84 :                 CPLAssert(poGeom->getGeometryType() == wkbPoint);
     727          84 :                 double dfDX = poGeom->toPoint()->getX() - adfXVals[nIdx];
     728          84 :                 double dfDY = poGeom->toPoint()->getY() - adfYVals[nIdx];
     729          84 :                 double dfDist2 = dfDX * dfDX + dfDY * dfDY;
     730          84 :                 if (dfDist2 > 0 && dfDist2 < dfDist2Min)
     731          24 :                     dfDist2Min = dfDist2;
     732             :             }
     733          18 :             if (dfDist2Min < std::numeric_limits<double>::max())
     734             :             {
     735          18 :                 dfSumDist2Min += dfDist2Min;
     736          18 :                 nCountDistMin++;
     737             :             }
     738             :         }
     739           3 :         poLyr->SetSpatialFilter(nullptr);
     740           3 :         if (nCountDistMin > 0)
     741             :         {
     742           3 :             const double dfNewRes = sqrt(dfSumDist2Min / nCountDistMin);
     743             :             // CPLDebug("GDAL", "dfRes = %f, dfNewRes = %f", dfRes, dfNewRes);
     744           3 :             dfRes = dfNewRes;
     745             :         }
     746             :     }
     747             : 
     748           6 :     if (!(dfRes > 0))
     749             :     {
     750           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid RESOLUTION");
     751           1 :         return nullptr;
     752             :     }
     753             : 
     754           5 :     constexpr double EPS = 1e-8;
     755           5 :     const double dfXSize =
     756           5 :         1 + std::floor((sEnvelope.MaxX - sEnvelope.MinX) / dfRes + EPS);
     757           5 :     if (dfXSize > std::numeric_limits<int>::max())
     758             :     {
     759           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow with dfXSize");
     760           1 :         return nullptr;
     761             :     }
     762           4 :     const int nXSize = std::max(2, static_cast<int>(dfXSize));
     763             : 
     764           4 :     const double dfYSize =
     765           4 :         1 + std::floor((sEnvelope.MaxY - sEnvelope.MinY) / dfRes + EPS);
     766           4 :     if (dfYSize > std::numeric_limits<int>::max())
     767             :     {
     768           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow with dfYSize");
     769           0 :         return nullptr;
     770             :     }
     771           4 :     const int nYSize = std::max(2, static_cast<int>(dfYSize));
     772             : 
     773           4 :     const double dfResX = (sEnvelope.MaxX - sEnvelope.MinX) / (nXSize - 1);
     774           4 :     const double dfResY = (sEnvelope.MaxY - sEnvelope.MinY) / (nYSize - 1);
     775             :     // CPLDebug("GDAL", "nXSize = %d, nYSize = %d", nXSize, nYSize);
     776             : 
     777           8 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
     778           4 :     const auto &apoSelfDims = GetDimensions();
     779           8 :     for (size_t i = 0; i < GetDimensionCount() - 1; ++i)
     780           4 :         apoNewDims.emplace_back(apoSelfDims[i]);
     781             : 
     782             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
     783           8 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH", nYSize);
     784             :     auto varY = GDALMDArrayRegularlySpaced::Create(
     785          12 :         std::string(), poDimY->GetName(), poDimY, sEnvelope.MinY, dfResY, 0);
     786           4 :     poDimY->SetIndexingVariable(varY);
     787             : 
     788             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
     789           8 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST", nXSize);
     790             :     auto varX = GDALMDArrayRegularlySpaced::Create(
     791          12 :         std::string(), poDimX->GetName(), poDimX, sEnvelope.MinX, dfResX, 0);
     792           4 :     poDimX->SetIndexingVariable(varX);
     793             : 
     794           4 :     apoNewDims.emplace_back(poDimY);
     795           4 :     apoNewDims.emplace_back(poDimX);
     796             : 
     797             :     const CPLStringList aosTokens(
     798           4 :         CSLTokenizeString2(osGridOptions.c_str(), ":", FALSE));
     799             : 
     800             :     // Extract nodata value from gridding options
     801           4 :     const char *pszNoDataValue = aosTokens.FetchNameValue("nodata");
     802           4 :     double dfNoDataValue = 0;
     803           4 :     if (pszNoDataValue != nullptr)
     804             :     {
     805           1 :         dfNoDataValue = CPLAtofM(pszNoDataValue);
     806             :     }
     807             : 
     808             :     // Extract radius from gridding options
     809           4 :     double dfRadius = 5 * std::max(dfResX, dfResY);
     810           4 :     const char *pszRadius = aosTokens.FetchNameValue("radius");
     811           4 :     if (pszRadius)
     812             :     {
     813           0 :         dfRadius = CPLAtofM(pszRadius);
     814             :     }
     815             :     else
     816             :     {
     817           4 :         const char *pszRadius1 = aosTokens.FetchNameValue("radius1");
     818           4 :         if (pszRadius1)
     819             :         {
     820           2 :             dfRadius = CPLAtofM(pszRadius1);
     821           2 :             const char *pszRadius2 = aosTokens.FetchNameValue("radius2");
     822           2 :             if (pszRadius2)
     823             :             {
     824           2 :                 dfRadius = std::max(dfRadius, CPLAtofM(pszRadius2));
     825             :             }
     826             :         }
     827             :     }
     828             : 
     829           8 :     return GDALMDArrayGridded::Create(
     830           4 :         self, apoNewDims, varX, varY, std::move(poDS), eAlg,
     831           4 :         std::move(poGridOptions), dfNoDataValue, sEnvelope.MinX, dfResX,
     832           4 :         sEnvelope.MinY, dfResY, dfRadius);
     833             : }

Generated by: LCOV version 1.14