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: 2024-11-21 22:18:42 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14