LCOV - code coverage report
Current view: top level - alg - gdalgrid.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1696 1843 92.0 %
Date: 2026-03-21 11:56:32 Functions: 31 31 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Gridding API.
       4             :  * Purpose:  Implementation of GDAL scattered data gridder.
       5             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdalgrid.h"
      16             : #include "gdalgrid_priv.h"
      17             : 
      18             : #include <cfloat>
      19             : #include <climits>
      20             : #include <cmath>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : 
      24             : #include <limits>
      25             : #include <map>
      26             : #include <utility>
      27             : #include <algorithm>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_cpu_features.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_multiproc.h"
      33             : #include "cpl_progress.h"
      34             : #include "cpl_quad_tree.h"
      35             : #include "cpl_string.h"
      36             : #include "cpl_vsi.h"
      37             : #include "cpl_worker_thread_pool.h"
      38             : #include "gdal.h"
      39             : #include "gdal_thread_pool.h"
      40             : 
      41             : constexpr double TO_RADIANS = M_PI / 180.0;
      42             : 
      43             : /************************************************************************/
      44             : /*                       GDALGridGetPointBounds()                       */
      45             : /************************************************************************/
      46             : 
      47     6475960 : static void GDALGridGetPointBounds(const void *hFeature, CPLRectObj *pBounds)
      48             : {
      49     6475960 :     const GDALGridPoint *psPoint = static_cast<const GDALGridPoint *>(hFeature);
      50     6475960 :     GDALGridXYArrays *psXYArrays = psPoint->psXYArrays;
      51     6475960 :     const int i = psPoint->i;
      52     6475960 :     const double dfX = psXYArrays->padfX[i];
      53     6475960 :     const double dfY = psXYArrays->padfY[i];
      54     6475960 :     pBounds->minx = dfX;
      55     6475960 :     pBounds->miny = dfY;
      56     6475960 :     pBounds->maxx = dfX;
      57     6475960 :     pBounds->maxy = dfY;
      58     6475960 : }
      59             : 
      60             : /************************************************************************/
      61             : /*                  GDALGridInverseDistanceToAPower()                   */
      62             : /************************************************************************/
      63             : 
      64             : /**
      65             :  * Inverse distance to a power.
      66             :  *
      67             :  * The Inverse Distance to a Power gridding method is a weighted average
      68             :  * interpolator. You should supply the input arrays with the scattered data
      69             :  * values including coordinates of every data point and output grid geometry.
      70             :  * The function will compute interpolated value for the given position in
      71             :  * output grid.
      72             :  *
      73             :  * For every grid node the resulting value \f$Z\f$ will be calculated using
      74             :  * formula:
      75             :  *
      76             :  * \f[
      77             :  *      Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}}
      78             :  * \f]
      79             :  *
      80             :  *  where
      81             :  *  <ul>
      82             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
      83             :  *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
      84             :  *           to point \f$i\f$,
      85             :  *      <li> \f$p\f$ is a weighting power,
      86             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
      87             :  *  </ul>
      88             :  *
      89             :  *  In this method the weighting factor \f$w\f$ is
      90             :  *
      91             :  *  \f[
      92             :  *      w=\frac{1}{r^p}
      93             :  *  \f]
      94             :  *
      95             :  * @param poOptionsIn Algorithm parameters. This should point to
      96             :  * GDALGridInverseDistanceToAPowerOptions object.
      97             :  * @param nPoints Number of elements in input arrays.
      98             :  * @param padfX Input array of X coordinates.
      99             :  * @param padfY Input array of Y coordinates.
     100             :  * @param padfZ Input array of Z values.
     101             :  * @param dfXPoint X coordinate of the point to compute.
     102             :  * @param dfYPoint Y coordinate of the point to compute.
     103             :  * @param pdfValue Pointer to variable where the computed grid node value
     104             :  * will be returned.
     105             :  * @param hExtraParamsIn extra parameters (unused)
     106             :  *
     107             :  * @return CE_None on success or CE_Failure if something goes wrong.
     108             :  */
     109             : 
     110      524688 : CPLErr GDALGridInverseDistanceToAPower(const void *poOptionsIn, GUInt32 nPoints,
     111             :                                        const double *padfX, const double *padfY,
     112             :                                        const double *padfZ, double dfXPoint,
     113             :                                        double dfYPoint, double *pdfValue,
     114             :                                        CPL_UNUSED void *hExtraParamsIn)
     115             : {
     116             :     // TODO: For optimization purposes pre-computed parameters should be moved
     117             :     // out of this routine to the calling function.
     118             : 
     119      524688 :     const GDALGridInverseDistanceToAPowerOptions *const poOptions =
     120             :         static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
     121             :             poOptionsIn);
     122             : 
     123             :     // Pre-compute search ellipse parameters.
     124      524688 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
     125      524688 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
     126      524688 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
     127             : 
     128             :     // Compute coefficients for coordinate system rotation.
     129      524688 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
     130      524688 :     const bool bRotated = dfAngle != 0.0;
     131      524688 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
     132      524688 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
     133             : 
     134      524688 :     const double dfPowerDiv2 = poOptions->dfPower / 2;
     135      524688 :     const double dfSmoothing = poOptions->dfSmoothing;
     136      524688 :     const GUInt32 nMaxPoints = poOptions->nMaxPoints;
     137      524688 :     double dfNominator = 0.0;
     138      524688 :     double dfDenominator = 0.0;
     139      524688 :     GUInt32 n = 0;
     140             : 
     141     3158800 :     for (GUInt32 i = 0; i < nPoints; i++)
     142             :     {
     143     2698240 :         double dfRX = padfX[i] - dfXPoint;
     144     2698240 :         double dfRY = padfY[i] - dfYPoint;
     145     2698240 :         const double dfR2 =
     146     2698240 :             dfRX * dfRX + dfRY * dfRY + dfSmoothing * dfSmoothing;
     147             : 
     148     2698240 :         if (bRotated)
     149             :         {
     150      327680 :             const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
     151      327680 :             const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
     152             : 
     153      327680 :             dfRX = dfRXRotated;
     154      327680 :             dfRY = dfRYRotated;
     155             :         }
     156             : 
     157             :         // Is this point located inside the search ellipse?
     158     2698240 :         if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
     159             :             dfR12Square)
     160             :         {
     161             :             // If the test point is close to the grid node, use the point
     162             :             // value directly as a node value to avoid singularity.
     163     1772110 :             if (dfR2 < 0.0000000000001)
     164             :             {
     165           0 :                 *pdfValue = padfZ[i];
     166           0 :                 return CE_None;
     167             :             }
     168             : 
     169     1772110 :             const double dfW = pow(dfR2, dfPowerDiv2);
     170     1772110 :             const double dfInvW = 1.0 / dfW;
     171     1772110 :             dfNominator += dfInvW * padfZ[i];
     172     1772110 :             dfDenominator += dfInvW;
     173     1772110 :             n++;
     174     1772110 :             if (nMaxPoints > 0 && n > nMaxPoints)
     175       64128 :                 break;
     176             :         }
     177             :     }
     178             : 
     179      524688 :     if (n < poOptions->nMinPoints || dfDenominator == 0.0)
     180             :     {
     181       34059 :         *pdfValue = poOptions->dfNoDataValue;
     182             :     }
     183             :     else
     184             :     {
     185      490629 :         *pdfValue = dfNominator / dfDenominator;
     186             :     }
     187             : 
     188      524688 :     return CE_None;
     189             : }
     190             : 
     191             : /************************************************************************/
     192             : /*           GDALGridInverseDistanceToAPowerNearestNeighbor()           */
     193             : /************************************************************************/
     194             : 
     195             : /**
     196             :  * Inverse distance to a power with nearest neighbor search, ideal when
     197             :  * max_points used.
     198             :  *
     199             :  * The Inverse Distance to a Power gridding method is a weighted average
     200             :  * interpolator. You should supply the input arrays with the scattered data
     201             :  * values including coordinates of every data point and output grid geometry.
     202             :  * The function will compute interpolated value for the given position in
     203             :  * output grid.
     204             :  *
     205             :  * For every grid node the resulting value \f$Z\f$ will be calculated using
     206             :  * formula for nearest matches:
     207             :  *
     208             :  * \f[
     209             :  *      Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}}
     210             :  * \f]
     211             :  *
     212             :  *  where
     213             :  *  <ul>
     214             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
     215             :  *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
     216             :  *           to point \f$i\f$ (with an optional smoothing parameter \f$s\f$),
     217             :  *      <li> \f$p\f$ is a weighting power,
     218             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
     219             :  *  </ul>
     220             :  *
     221             :  *  In this method the weighting factor \f$w\f$ is
     222             :  *
     223             :  *  \f[
     224             :  *      w=\frac{1}{r^p}
     225             :  *  \f]
     226             :  *
     227             :  * @param poOptionsIn Algorithm parameters. This should point to
     228             :  * GDALGridInverseDistanceToAPowerNearestNeighborOptions object.
     229             :  * @param nPoints Number of elements in input arrays.
     230             :  * @param padfX Input array of X coordinates.
     231             :  * @param padfY Input array of Y coordinates.
     232             :  * @param padfZ Input array of Z values.
     233             :  * @param dfXPoint X coordinate of the point to compute.
     234             :  * @param dfYPoint Y coordinate of the point to compute.
     235             :  * @param pdfValue Pointer to variable where the computed grid node value
     236             :  * will be returned.
     237             :  * @param hExtraParamsIn extra parameters.
     238             :  *
     239             :  * @return CE_None on success or CE_Failure if something goes wrong.
     240             :  */
     241             : 
     242      459952 : CPLErr GDALGridInverseDistanceToAPowerNearestNeighbor(
     243             :     const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
     244             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
     245             :     double *pdfValue, void *hExtraParamsIn)
     246             : {
     247      459952 :     CPL_IGNORE_RET_VAL(nPoints);
     248             : 
     249             :     const GDALGridInverseDistanceToAPowerNearestNeighborOptions *const
     250      459952 :         poOptions = static_cast<
     251             :             const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
     252             :             poOptionsIn);
     253      459952 :     const double dfRadius = poOptions->dfRadius;
     254      459952 :     const double dfSmoothing = poOptions->dfSmoothing;
     255      459952 :     const double dfSmoothing2 = dfSmoothing * dfSmoothing;
     256             : 
     257      459952 :     const GUInt32 nMaxPoints = poOptions->nMaxPoints;
     258             : 
     259      459952 :     GDALGridExtraParameters *psExtraParams =
     260             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
     261      459952 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
     262      459952 :     CPLAssert(phQuadTree);
     263             : 
     264      459952 :     const double dfRPower2 = psExtraParams->dfRadiusPower2PreComp;
     265      459952 :     const double dfPowerDiv2 = psExtraParams->dfPowerDiv2PreComp;
     266             : 
     267      919904 :     std::multimap<double, double> oMapDistanceToZValues;
     268             : 
     269      459952 :     const double dfSearchRadius = dfRadius;
     270             :     CPLRectObj sAoi;
     271      459952 :     sAoi.minx = dfXPoint - dfSearchRadius;
     272      459952 :     sAoi.miny = dfYPoint - dfSearchRadius;
     273      459952 :     sAoi.maxx = dfXPoint + dfSearchRadius;
     274      459952 :     sAoi.maxy = dfYPoint + dfSearchRadius;
     275      459952 :     int nFeatureCount = 0;
     276             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
     277      459952 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
     278      459952 :     if (nFeatureCount != 0)
     279             :     {
     280     2805310 :         for (int k = 0; k < nFeatureCount; k++)
     281             :         {
     282     2345360 :             const int i = papsPoints[k]->i;
     283     2345360 :             const double dfRX = padfX[i] - dfXPoint;
     284     2345360 :             const double dfRY = padfY[i] - dfYPoint;
     285             : 
     286     2345360 :             const double dfR2 = dfRX * dfRX + dfRY * dfRY;
     287             :             // real distance + smoothing
     288     2345360 :             const double dfRsmoothed2 = dfR2 + dfSmoothing2;
     289     2345360 :             if (dfRsmoothed2 < 0.0000000000001)
     290             :             {
     291           0 :                 *pdfValue = padfZ[i];
     292           0 :                 CPLFree(papsPoints);
     293           0 :                 return CE_None;
     294             :             }
     295             :             // is point within real distance?
     296     2345360 :             if (dfR2 <= dfRPower2)
     297             :             {
     298             :                 oMapDistanceToZValues.insert(
     299     2270250 :                     std::make_pair(dfRsmoothed2, padfZ[i]));
     300             :             }
     301             :         }
     302             :     }
     303      459952 :     CPLFree(papsPoints);
     304             : 
     305      459952 :     double dfNominator = 0.0;
     306      459952 :     double dfDenominator = 0.0;
     307      459952 :     GUInt32 n = 0;
     308             : 
     309             :     // Examine all "neighbors" within the radius (sorted by distance via the
     310             :     // multimap), and use the closest n points based on distance until the max
     311             :     // is reached.
     312     1912690 :     for (std::multimap<double, double>::iterator oMapDistanceToZValuesIter =
     313      459952 :              oMapDistanceToZValues.begin();
     314     2372640 :          oMapDistanceToZValuesIter != oMapDistanceToZValues.end();
     315     1912690 :          ++oMapDistanceToZValuesIter)
     316             :     {
     317     1979020 :         const double dfR2 = oMapDistanceToZValuesIter->first;
     318     1979020 :         const double dfZ = oMapDistanceToZValuesIter->second;
     319             : 
     320     1979020 :         const double dfW = pow(dfR2, dfPowerDiv2);
     321     1979020 :         const double dfInvW = 1.0 / dfW;
     322     1979020 :         dfNominator += dfInvW * dfZ;
     323     1979020 :         dfDenominator += dfInvW;
     324     1979020 :         n++;
     325     1979020 :         if (nMaxPoints > 0 && n >= nMaxPoints)
     326             :         {
     327       66336 :             break;
     328             :         }
     329             :     }
     330             : 
     331      459952 :     if (n < poOptions->nMinPoints || dfDenominator == 0.0)
     332             :     {
     333       65620 :         *pdfValue = poOptions->dfNoDataValue;
     334             :     }
     335             :     else
     336             :     {
     337      394332 :         *pdfValue = dfNominator / dfDenominator;
     338             :     }
     339             : 
     340      459952 :     return CE_None;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*     GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant()      */
     345             : /************************************************************************/
     346             : 
     347             : /**
     348             :  * Inverse distance to a power with nearest neighbor search, with a per-quadrant
     349             :  * search logic.
     350             :  */
     351      262152 : static CPLErr GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant(
     352             :     const void *poOptionsIn, GUInt32 /*nPoints*/, const double *padfX,
     353             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
     354             :     double *pdfValue, void *hExtraParamsIn)
     355             : {
     356             :     const GDALGridInverseDistanceToAPowerNearestNeighborOptions *const
     357      262152 :         poOptions = static_cast<
     358             :             const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
     359             :             poOptionsIn);
     360      262152 :     const double dfRadius = poOptions->dfRadius;
     361      262152 :     const double dfSmoothing = poOptions->dfSmoothing;
     362      262152 :     const double dfSmoothing2 = dfSmoothing * dfSmoothing;
     363             : 
     364      262152 :     const GUInt32 nMaxPoints = poOptions->nMaxPoints;
     365      262152 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
     366      262152 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
     367             : 
     368      262152 :     GDALGridExtraParameters *psExtraParams =
     369             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
     370      262152 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
     371      262152 :     CPLAssert(phQuadTree);
     372             : 
     373      262152 :     const double dfRPower2 = psExtraParams->dfRadiusPower2PreComp;
     374      262152 :     const double dfPowerDiv2 = psExtraParams->dfPowerDiv2PreComp;
     375     2621520 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
     376             : 
     377      262152 :     const double dfSearchRadius = dfRadius;
     378             :     CPLRectObj sAoi;
     379      262152 :     sAoi.minx = dfXPoint - dfSearchRadius;
     380      262152 :     sAoi.miny = dfYPoint - dfSearchRadius;
     381      262152 :     sAoi.maxx = dfXPoint + dfSearchRadius;
     382      262152 :     sAoi.maxy = dfYPoint + dfSearchRadius;
     383      262152 :     int nFeatureCount = 0;
     384             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
     385      262152 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
     386      262152 :     if (nFeatureCount != 0)
     387             :     {
     388     1572910 :         for (int k = 0; k < nFeatureCount; k++)
     389             :         {
     390     1310760 :             const int i = papsPoints[k]->i;
     391     1310760 :             const double dfRX = padfX[i] - dfXPoint;
     392     1310760 :             const double dfRY = padfY[i] - dfYPoint;
     393             : 
     394     1310760 :             const double dfR2 = dfRX * dfRX + dfRY * dfRY;
     395             :             // real distance + smoothing
     396     1310760 :             const double dfRsmoothed2 = dfR2 + dfSmoothing2;
     397     1310760 :             if (dfRsmoothed2 < 0.0000000000001)
     398             :             {
     399           0 :                 *pdfValue = padfZ[i];
     400           0 :                 CPLFree(papsPoints);
     401           0 :                 return CE_None;
     402             :             }
     403             :             // is point within real distance?
     404     1310760 :             if (dfR2 <= dfRPower2)
     405             :             {
     406     1187330 :                 const int iQuadrant =
     407     1187330 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
     408             :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
     409     1187330 :                     std::make_pair(dfRsmoothed2, padfZ[i]));
     410             :             }
     411             :         }
     412             :     }
     413      262152 :     CPLFree(papsPoints);
     414             : 
     415             :     std::multimap<double, double>::iterator aoIter[] = {
     416      262152 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
     417      262152 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
     418      262152 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
     419      262152 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
     420      262152 :     };
     421      262152 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
     422             : 
     423             :     // Examine all "neighbors" within the radius (sorted by distance via the
     424             :     // multimap), and use the closest n points based on distance until the max
     425             :     // is reached.
     426             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
     427             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
     428             :     // point in quarant 0, etc.
     429      262152 :     int nQuadrantIterFinishedFlag = 0;
     430      262152 :     GUInt32 anPerQuadrant[4] = {0};
     431      262152 :     double dfNominator = 0.0;
     432      262152 :     double dfDenominator = 0.0;
     433      262152 :     GUInt32 n = 0;
     434      262152 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
     435             :     {
     436     2488980 :         if (aoIter[iQuadrant] ==
     437     5571620 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
     438      593666 :             (nMaxPointsPerQuadrant > 0 &&
     439      593666 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
     440             :         {
     441     1408660 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
     442     1408660 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
     443      262151 :                 break;
     444     1146500 :             continue;
     445             :         }
     446             : 
     447     1080320 :         const double dfR2 = aoIter[iQuadrant]->first;
     448     1080320 :         const double dfZ = aoIter[iQuadrant]->second;
     449     1080320 :         ++aoIter[iQuadrant];
     450             : 
     451     1080320 :         const double dfW = pow(dfR2, dfPowerDiv2);
     452     1080320 :         const double dfInvW = 1.0 / dfW;
     453     1080320 :         dfNominator += dfInvW * dfZ;
     454     1080320 :         dfDenominator += dfInvW;
     455     1080320 :         n++;
     456     1080320 :         anPerQuadrant[iQuadrant]++;
     457     1080320 :         if (nMaxPoints > 0 && n >= nMaxPoints)
     458             :         {
     459           1 :             break;
     460             :         }
     461     2226830 :     }
     462             : 
     463      262152 :     if (nMinPointsPerQuadrant > 0 &&
     464      131080 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
     465       52831 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
     466       42894 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
     467       33634 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
     468             :     {
     469      101515 :         *pdfValue = poOptions->dfNoDataValue;
     470             :     }
     471      160637 :     else if (n < poOptions->nMinPoints || dfDenominator == 0.0)
     472             :     {
     473           1 :         *pdfValue = poOptions->dfNoDataValue;
     474             :     }
     475             :     else
     476             :     {
     477      160636 :         *pdfValue = dfNominator / dfDenominator;
     478             :     }
     479             : 
     480      262152 :     return CE_None;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*              GDALGridInverseDistanceToAPowerNoSearch()               */
     485             : /************************************************************************/
     486             : 
     487             : /**
     488             :  * Inverse distance to a power for whole data set.
     489             :  *
     490             :  * This is somewhat optimized version of the Inverse Distance to a Power
     491             :  * method. It is used when the search ellips is not set. The algorithm and
     492             :  * parameters are the same as in GDALGridInverseDistanceToAPower(), but this
     493             :  * implementation works faster, because of no search.
     494             :  *
     495             :  * @see GDALGridInverseDistanceToAPower()
     496             :  */
     497             : 
     498      131573 : CPLErr GDALGridInverseDistanceToAPowerNoSearch(
     499             :     const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
     500             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
     501             :     double *pdfValue, void * /* hExtraParamsIn */)
     502             : {
     503      131573 :     const GDALGridInverseDistanceToAPowerOptions *const poOptions =
     504             :         static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
     505             :             poOptionsIn);
     506      131573 :     const double dfPowerDiv2 = poOptions->dfPower / 2.0;
     507      131573 :     const double dfSmoothing = poOptions->dfSmoothing;
     508      131573 :     const double dfSmoothing2 = dfSmoothing * dfSmoothing;
     509      131573 :     double dfNominator = 0.0;
     510      131573 :     double dfDenominator = 0.0;
     511      131573 :     const bool bPower2 = dfPowerDiv2 == 1.0;
     512             : 
     513      131573 :     GUInt32 i = 0;  // Used after if.
     514      131573 :     if (bPower2)
     515             :     {
     516       66037 :         if (dfSmoothing2 > 0)
     517             :         {
     518      393216 :             for (i = 0; i < nPoints; i++)
     519             :             {
     520      327680 :                 const double dfRX = padfX[i] - dfXPoint;
     521      327680 :                 const double dfRY = padfY[i] - dfYPoint;
     522      327680 :                 const double dfR2 = dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
     523             : 
     524      327680 :                 const double dfInvR2 = 1.0 / dfR2;
     525      327680 :                 dfNominator += dfInvR2 * padfZ[i];
     526      327680 :                 dfDenominator += dfInvR2;
     527             :             }
     528             :         }
     529             :         else
     530             :         {
     531       80401 :             for (i = 0; i < nPoints; i++)
     532             :             {
     533       80301 :                 const double dfRX = padfX[i] - dfXPoint;
     534       80301 :                 const double dfRY = padfY[i] - dfYPoint;
     535       80301 :                 const double dfR2 = dfRX * dfRX + dfRY * dfRY;
     536             : 
     537             :                 // If the test point is close to the grid node, use the point
     538             :                 // value directly as a node value to avoid singularity.
     539       80301 :                 if (dfR2 < 0.0000000000001)
     540             :                 {
     541         401 :                     break;
     542             :                 }
     543             : 
     544       79900 :                 const double dfInvR2 = 1.0 / dfR2;
     545       79900 :                 dfNominator += dfInvR2 * padfZ[i];
     546       79900 :                 dfDenominator += dfInvR2;
     547             :             }
     548             :         }
     549             :     }
     550             :     else
     551             :     {
     552      393216 :         for (i = 0; i < nPoints; i++)
     553             :         {
     554      327680 :             const double dfRX = padfX[i] - dfXPoint;
     555      327680 :             const double dfRY = padfY[i] - dfYPoint;
     556      327680 :             const double dfR2 = dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
     557             : 
     558             :             // If the test point is close to the grid node, use the point
     559             :             // value directly as a node value to avoid singularity.
     560      327680 :             if (dfR2 < 0.0000000000001)
     561             :             {
     562           0 :                 break;
     563             :             }
     564             : 
     565      327680 :             const double dfW = pow(dfR2, dfPowerDiv2);
     566      327680 :             const double dfInvW = 1.0 / dfW;
     567      327680 :             dfNominator += dfInvW * padfZ[i];
     568      327680 :             dfDenominator += dfInvW;
     569             :         }
     570             :     }
     571             : 
     572      131573 :     if (i != nPoints)
     573             :     {
     574         401 :         *pdfValue = padfZ[i];
     575             :     }
     576      131172 :     else if (dfDenominator == 0.0)
     577             :     {
     578           0 :         *pdfValue = poOptions->dfNoDataValue;
     579             :     }
     580             :     else
     581             :     {
     582      131172 :         *pdfValue = dfNominator / dfDenominator;
     583             :     }
     584             : 
     585      131573 :     return CE_None;
     586             : }
     587             : 
     588             : /************************************************************************/
     589             : /*                       GDALGridMovingAverage()                        */
     590             : /************************************************************************/
     591             : 
     592             : /**
     593             :  * Moving average.
     594             :  *
     595             :  * The Moving Average is a simple data averaging algorithm. It uses a moving
     596             :  * window of elliptic form to search values and averages all data points
     597             :  * within the window. Search ellipse can be rotated by specified angle, the
     598             :  * center of ellipse located at the grid node. Also the minimum number of data
     599             :  * points to average can be set, if there are not enough points in window, the
     600             :  * grid node considered empty and will be filled with specified NODATA value.
     601             :  *
     602             :  * Mathematically it can be expressed with the formula:
     603             :  *
     604             :  * \f[
     605             :  *      Z=\frac{\sum_{i=1}^n{Z_i}}{n}
     606             :  * \f]
     607             :  *
     608             :  *  where
     609             :  *  <ul>
     610             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
     611             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
     612             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
     613             :  *  </ul>
     614             :  *
     615             :  * @param poOptionsIn Algorithm parameters. This should point to
     616             :  * GDALGridMovingAverageOptions object.
     617             :  * @param nPoints Number of elements in input arrays.
     618             :  * @param padfX Input array of X coordinates.
     619             :  * @param padfY Input array of Y coordinates.
     620             :  * @param padfZ Input array of Z values.
     621             :  * @param dfXPoint X coordinate of the point to compute.
     622             :  * @param dfYPoint Y coordinate of the point to compute.
     623             :  * @param pdfValue Pointer to variable where the computed grid node value
     624             :  * will be returned.
     625             :  * @param hExtraParamsIn extra parameters (unused)
     626             :  *
     627             :  * @return CE_None on success or CE_Failure if something goes wrong.
     628             :  */
     629             : 
     630      657777 : CPLErr GDALGridMovingAverage(const void *poOptionsIn, GUInt32 nPoints,
     631             :                              const double *padfX, const double *padfY,
     632             :                              const double *padfZ, double dfXPoint,
     633             :                              double dfYPoint, double *pdfValue,
     634             :                              CPL_UNUSED void *hExtraParamsIn)
     635             : {
     636             :     // TODO: For optimization purposes pre-computed parameters should be moved
     637             :     // out of this routine to the calling function.
     638             : 
     639      657777 :     const GDALGridMovingAverageOptions *const poOptions =
     640             :         static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
     641             :     // Pre-compute search ellipse parameters.
     642      657777 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
     643      657777 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
     644             :     const double dfSearchRadius =
     645      657777 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
     646      657777 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
     647             : 
     648      657777 :     GDALGridExtraParameters *psExtraParams =
     649             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
     650      657777 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
     651             : 
     652             :     // Compute coefficients for coordinate system rotation.
     653      657777 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
     654      657777 :     const bool bRotated = dfAngle != 0.0;
     655             : 
     656      657777 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
     657      657777 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
     658             : 
     659      657777 :     double dfAccumulator = 0.0;
     660             : 
     661      657777 :     GUInt32 n = 0;  // Used after for.
     662      657777 :     if (phQuadTree != nullptr)
     663             :     {
     664             :         CPLRectObj sAoi;
     665        2002 :         sAoi.minx = dfXPoint - dfSearchRadius;
     666        2002 :         sAoi.miny = dfYPoint - dfSearchRadius;
     667        2002 :         sAoi.maxx = dfXPoint + dfSearchRadius;
     668        2002 :         sAoi.maxy = dfYPoint + dfSearchRadius;
     669        2002 :         int nFeatureCount = 0;
     670             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
     671        2002 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
     672        2002 :         if (nFeatureCount != 0)
     673             :         {
     674       77604 :             for (int k = 0; k < nFeatureCount; k++)
     675             :             {
     676       75602 :                 const int i = papsPoints[k]->i;
     677       75602 :                 double dfRX = padfX[i] - dfXPoint;
     678       75602 :                 double dfRY = padfY[i] - dfYPoint;
     679             : 
     680       75602 :                 if (bRotated)
     681             :                 {
     682       36103 :                     const double dfRXRotated =
     683       36103 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
     684       36103 :                     const double dfRYRotated =
     685       36103 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
     686       36103 :                     dfRX = dfRXRotated;
     687       36103 :                     dfRY = dfRYRotated;
     688             :                 }
     689             : 
     690       75602 :                 if (dfRadius2Square * dfRX * dfRX +
     691       75602 :                         dfRadius1Square * dfRY * dfRY <=
     692             :                     dfR12Square)
     693             :                 {
     694       41560 :                     dfAccumulator += padfZ[i];
     695       41560 :                     n++;
     696             :                 }
     697             :             }
     698             :         }
     699        2002 :         CPLFree(papsPoints);
     700             :     }
     701             :     else
     702             :     {
     703     4092660 :         for (GUInt32 i = 0; i < nPoints; i++)
     704             :         {
     705     3436890 :             double dfRX = padfX[i] - dfXPoint;
     706     3436890 :             double dfRY = padfY[i] - dfYPoint;
     707             : 
     708     3436890 :             if (bRotated)
     709             :             {
     710      327680 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
     711      327680 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
     712             : 
     713      327680 :                 dfRX = dfRXRotated;
     714      327680 :                 dfRY = dfRYRotated;
     715             :             }
     716             : 
     717             :             // Is this point located inside the search ellipse?
     718     3436890 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
     719             :                 dfR12Square)
     720             :             {
     721     2778340 :                 dfAccumulator += padfZ[i];
     722     2778340 :                 n++;
     723             :             }
     724             :         }
     725             :     }
     726             : 
     727      657777 :     if (n < poOptions->nMinPoints || n == 0)
     728             :     {
     729       84254 :         *pdfValue = poOptions->dfNoDataValue;
     730             :     }
     731             :     else
     732             :     {
     733      573523 :         *pdfValue = dfAccumulator / n;
     734             :     }
     735             : 
     736      657777 :     return CE_None;
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                  GDALGridMovingAveragePerQuadrant()                  */
     741             : /************************************************************************/
     742             : 
     743             : /**
     744             :  * Moving average, with a per-quadrant search logic.
     745             :  */
     746      196616 : static CPLErr GDALGridMovingAveragePerQuadrant(
     747             :     const void *poOptionsIn, GUInt32 /*nPoints*/, const double *padfX,
     748             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
     749             :     double *pdfValue, void *hExtraParamsIn)
     750             : {
     751      196616 :     const GDALGridMovingAverageOptions *const poOptions =
     752             :         static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
     753      196616 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
     754      196616 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
     755      196616 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
     756             : 
     757      196616 :     const GUInt32 nMaxPoints = poOptions->nMaxPoints;
     758      196616 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
     759      196616 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
     760             : 
     761             :     // Compute coefficients for coordinate system rotation.
     762      196616 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
     763      196616 :     const bool bRotated = dfAngle != 0.0;
     764      196616 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
     765      196616 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
     766             : 
     767      196616 :     GDALGridExtraParameters *psExtraParams =
     768             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
     769      196616 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
     770      196616 :     CPLAssert(phQuadTree);
     771             : 
     772     1966160 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
     773             : 
     774             :     const double dfSearchRadius =
     775      196616 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
     776             :     CPLRectObj sAoi;
     777      196616 :     sAoi.minx = dfXPoint - dfSearchRadius;
     778      196616 :     sAoi.miny = dfYPoint - dfSearchRadius;
     779      196616 :     sAoi.maxx = dfXPoint + dfSearchRadius;
     780      196616 :     sAoi.maxy = dfYPoint + dfSearchRadius;
     781      196616 :     int nFeatureCount = 0;
     782             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
     783      196616 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
     784      196616 :     if (nFeatureCount != 0)
     785             :     {
     786     1179690 :         for (int k = 0; k < nFeatureCount; k++)
     787             :         {
     788      983074 :             const int i = papsPoints[k]->i;
     789      983074 :             double dfRX = padfX[i] - dfXPoint;
     790      983074 :             double dfRY = padfY[i] - dfYPoint;
     791             : 
     792      983074 :             if (bRotated)
     793             :             {
     794           4 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
     795           4 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
     796           4 :                 dfRX = dfRXRotated;
     797           4 :                 dfRY = dfRYRotated;
     798             :             }
     799             : 
     800      983074 :             const double dfRXSquare = dfRX * dfRX;
     801      983074 :             const double dfRYSquare = dfRY * dfRY;
     802             : 
     803      983074 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
     804             :                 dfR12Square)
     805             :             {
     806      983067 :                 const int iQuadrant =
     807      983067 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
     808             :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
     809      983067 :                     std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
     810             :             }
     811             :         }
     812             :     }
     813      196616 :     CPLFree(papsPoints);
     814             : 
     815             :     std::multimap<double, double>::iterator aoIter[] = {
     816      196616 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
     817      196616 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
     818      196616 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
     819      196616 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
     820      196616 :     };
     821      196616 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
     822             : 
     823             :     // Examine all "neighbors" within the radius (sorted by distance via the
     824             :     // multimap), and use the closest n points based on distance until the max
     825             :     // is reached.
     826             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
     827             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
     828             :     // point in quarant 0, etc.
     829      196616 :     int nQuadrantIterFinishedFlag = 0;
     830      196616 :     GUInt32 anPerQuadrant[4] = {0};
     831      196616 :     double dfNominator = 0.0;
     832      196616 :     GUInt32 n = 0;
     833      196616 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
     834             :     {
     835      983112 :         if (aoIter[iQuadrant] ==
     836     2293920 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
     837      327695 :             (nMaxPointsPerQuadrant > 0 &&
     838      327695 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
     839             :         {
     840      262191 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
     841      262191 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
     842       65543 :                 break;
     843      196648 :             continue;
     844             :         }
     845             : 
     846      720921 :         const double dfZ = aoIter[iQuadrant]->second;
     847      720921 :         ++aoIter[iQuadrant];
     848             : 
     849      720921 :         dfNominator += dfZ;
     850      720921 :         n++;
     851      720921 :         anPerQuadrant[iQuadrant]++;
     852      720921 :         if (nMaxPoints > 0 && n >= nMaxPoints)
     853             :         {
     854      131073 :             break;
     855             :         }
     856      786496 :     }
     857             : 
     858      196616 :     if (nMinPointsPerQuadrant > 0 &&
     859      131078 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
     860      131077 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
     861      131076 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
     862      131076 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
     863             :     {
     864       65538 :         *pdfValue = poOptions->dfNoDataValue;
     865             :     }
     866      131078 :     else if (n < poOptions->nMinPoints || n == 0)
     867             :     {
     868           1 :         *pdfValue = poOptions->dfNoDataValue;
     869             :     }
     870             :     else
     871             :     {
     872      131077 :         *pdfValue = dfNominator / n;
     873             :     }
     874             : 
     875      393232 :     return CE_None;
     876             : }
     877             : 
     878             : /************************************************************************/
     879             : /*                      GDALGridNearestNeighbor()                       */
     880             : /************************************************************************/
     881             : 
     882             : /**
     883             :  * Nearest neighbor.
     884             :  *
     885             :  * The Nearest Neighbor method doesn't perform any interpolation or smoothing,
     886             :  * it just takes the value of nearest point found in grid node search ellipse
     887             :  * and returns it as a result. If there are no points found, the specified
     888             :  * NODATA value will be returned.
     889             :  *
     890             :  * @param poOptionsIn Algorithm parameters. This should point to
     891             :  * GDALGridNearestNeighborOptions object.
     892             :  * @param nPoints Number of elements in input arrays.
     893             :  * @param padfX Input array of X coordinates.
     894             :  * @param padfY Input array of Y coordinates.
     895             :  * @param padfZ Input array of Z values.
     896             :  * @param dfXPoint X coordinate of the point to compute.
     897             :  * @param dfYPoint Y coordinate of the point to compute.
     898             :  * @param pdfValue Pointer to variable where the computed grid node value
     899             :  * will be returned.
     900             :  * @param hExtraParamsIn extra parameters.
     901             :  *
     902             :  * @return CE_None on success or CE_Failure if something goes wrong.
     903             :  */
     904             : 
     905      500354 : CPLErr GDALGridNearestNeighbor(const void *poOptionsIn, GUInt32 nPoints,
     906             :                                const double *padfX, const double *padfY,
     907             :                                const double *padfZ, double dfXPoint,
     908             :                                double dfYPoint, double *pdfValue,
     909             :                                void *hExtraParamsIn)
     910             : {
     911             :     // TODO: For optimization purposes pre-computed parameters should be moved
     912             :     // out of this routine to the calling function.
     913             : 
     914      500354 :     const GDALGridNearestNeighborOptions *const poOptions =
     915             :         static_cast<const GDALGridNearestNeighborOptions *>(poOptionsIn);
     916             :     // Pre-compute search ellipse parameters.
     917      500354 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
     918      500354 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
     919      500354 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
     920      500354 :     GDALGridExtraParameters *psExtraParams =
     921             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
     922      500354 :     CPLQuadTree *hQuadTree = psExtraParams->hQuadTree;
     923             : 
     924             :     // Compute coefficients for coordinate system rotation.
     925      500354 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
     926      500354 :     const bool bRotated = dfAngle != 0.0;
     927      500354 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
     928      500354 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
     929             : 
     930             :     // If the nearest point will not be found, its value remains as NODATA.
     931      500354 :     double dfNearestValue = poOptions->dfNoDataValue;
     932      500354 :     GUInt32 i = 0;
     933             : 
     934      500354 :     double dfSearchRadius = psExtraParams->dfInitialSearchRadius;
     935      500354 :     if (hQuadTree != nullptr)
     936             :     {
     937      142179 :         if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
     938       67102 :             dfSearchRadius =
     939       67102 :                 std::max(poOptions->dfRadius1, poOptions->dfRadius2);
     940             :         CPLRectObj sAoi;
     941      358226 :         while (dfSearchRadius > 0)
     942             :         {
     943      358226 :             sAoi.minx = dfXPoint - dfSearchRadius;
     944      358226 :             sAoi.miny = dfYPoint - dfSearchRadius;
     945      358226 :             sAoi.maxx = dfXPoint + dfSearchRadius;
     946      358226 :             sAoi.maxy = dfYPoint + dfSearchRadius;
     947      358226 :             int nFeatureCount = 0;
     948             :             GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
     949      358226 :                 CPLQuadTreeSearch(hQuadTree, &sAoi, &nFeatureCount));
     950      358226 :             if (nFeatureCount != 0)
     951             :             {
     952             :                 // Nearest distance will be initialized with the distance to the
     953             :                 // first point in array.
     954       78477 :                 double dfNearestRSquare = std::numeric_limits<double>::max();
     955      413634 :                 for (int k = 0; k < nFeatureCount; k++)
     956             :                 {
     957      335157 :                     const int idx = papsPoints[k]->i;
     958      335157 :                     const double dfRX = padfX[idx] - dfXPoint;
     959      335157 :                     const double dfRY = padfY[idx] - dfYPoint;
     960             : 
     961      335157 :                     const double dfR2 = dfRX * dfRX + dfRY * dfRY;
     962      335157 :                     if (dfR2 <= dfNearestRSquare)
     963             :                     {
     964      178380 :                         dfNearestRSquare = dfR2;
     965      178380 :                         dfNearestValue = padfZ[idx];
     966             :                     }
     967             :                 }
     968             : 
     969       78477 :                 CPLFree(papsPoints);
     970      142179 :                 break;
     971             :             }
     972             : 
     973      279749 :             CPLFree(papsPoints);
     974      279749 :             if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
     975             :                 break;
     976      216047 :             dfSearchRadius *= 2;
     977             : #if DEBUG_VERBOSE
     978             :             CPLDebug("GDAL_GRID", "Increasing search radius to %.16g",
     979             :                      dfSearchRadius);
     980             : #endif
     981             :         }
     982             :     }
     983             :     else
     984             :     {
     985      358175 :         double dfNearestRSquare = std::numeric_limits<double>::max();
     986   431186000 :         while (i < nPoints)
     987             :         {
     988   430828000 :             double dfRX = padfX[i] - dfXPoint;
     989   430828000 :             double dfRY = padfY[i] - dfYPoint;
     990             : 
     991   430828000 :             if (bRotated)
     992             :             {
     993           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
     994           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
     995             : 
     996           0 :                 dfRX = dfRXRotated;
     997           0 :                 dfRY = dfRYRotated;
     998             :             }
     999             : 
    1000             :             // Is this point located inside the search ellipse?
    1001   430828000 :             const double dfRXSquare = dfRX * dfRX;
    1002   430828000 :             const double dfRYSquare = dfRY * dfRY;
    1003   430828000 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
    1004             :                 dfR12Square)
    1005             :             {
    1006   429858000 :                 const double dfR2 = dfRXSquare + dfRYSquare;
    1007   429858000 :                 if (dfR2 <= dfNearestRSquare)
    1008             :                 {
    1009    17134800 :                     dfNearestRSquare = dfR2;
    1010    17134800 :                     dfNearestValue = padfZ[i];
    1011             :                 }
    1012             :             }
    1013             : 
    1014   430828000 :             i++;
    1015             :         }
    1016             :     }
    1017             : 
    1018      500354 :     *pdfValue = dfNearestValue;
    1019             : 
    1020      500354 :     return CE_None;
    1021             : }
    1022             : 
    1023             : /************************************************************************/
    1024             : /*                     GDALGridDataMetricMinimum()                      */
    1025             : /************************************************************************/
    1026             : 
    1027             : /**
    1028             :  * Minimum data value (data metric).
    1029             :  *
    1030             :  * Minimum value found in grid node search ellipse. If there are no points
    1031             :  * found, the specified NODATA value will be returned.
    1032             :  *
    1033             :  * \f[
    1034             :  *      Z=\min{(Z_1,Z_2,\ldots,Z_n)}
    1035             :  * \f]
    1036             :  *
    1037             :  *  where
    1038             :  *  <ul>
    1039             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    1040             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
    1041             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    1042             :  *  </ul>
    1043             :  *
    1044             :  * @param poOptionsIn Algorithm parameters. This should point to
    1045             :  * GDALGridDataMetricsOptions object.
    1046             :  * @param nPoints Number of elements in input arrays.
    1047             :  * @param padfX Input array of X coordinates.
    1048             :  * @param padfY Input array of Y coordinates.
    1049             :  * @param padfZ Input array of Z values.
    1050             :  * @param dfXPoint X coordinate of the point to compute.
    1051             :  * @param dfYPoint Y coordinate of the point to compute.
    1052             :  * @param pdfValue Pointer to variable where the computed grid node value
    1053             :  * will be returned.
    1054             :  * @param hExtraParamsIn unused.
    1055             :  *
    1056             :  * @return CE_None on success or CE_Failure if something goes wrong.
    1057             :  */
    1058             : 
    1059      330082 : CPLErr GDALGridDataMetricMinimum(const void *poOptionsIn, GUInt32 nPoints,
    1060             :                                  const double *padfX, const double *padfY,
    1061             :                                  const double *padfZ, double dfXPoint,
    1062             :                                  double dfYPoint, double *pdfValue,
    1063             :                                  void *hExtraParamsIn)
    1064             : {
    1065             :     // TODO: For optimization purposes pre-computed parameters should be moved
    1066             :     // out of this routine to the calling function.
    1067             : 
    1068      330082 :     const GDALGridDataMetricsOptions *const poOptions =
    1069             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1070             : 
    1071             :     // Pre-compute search ellipse parameters.
    1072      330082 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1073      330082 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1074             :     const double dfSearchRadius =
    1075      330082 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1076      330082 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1077             : 
    1078      330082 :     GDALGridExtraParameters *psExtraParams =
    1079             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1080      330082 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1081             : 
    1082             :     // Compute coefficients for coordinate system rotation.
    1083      330082 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1084      330082 :     const bool bRotated = dfAngle != 0.0;
    1085      330082 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1086      330082 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1087             : 
    1088      330082 :     double dfMinimumValue = std::numeric_limits<double>::max();
    1089      330082 :     GUInt32 n = 0;
    1090      330082 :     if (phQuadTree != nullptr)
    1091             :     {
    1092             :         CPLRectObj sAoi;
    1093        2002 :         sAoi.minx = dfXPoint - dfSearchRadius;
    1094        2002 :         sAoi.miny = dfYPoint - dfSearchRadius;
    1095        2002 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    1096        2002 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    1097        2002 :         int nFeatureCount = 0;
    1098             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1099        2002 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1100        2002 :         if (nFeatureCount != 0)
    1101             :         {
    1102       83100 :             for (int k = 0; k < nFeatureCount; k++)
    1103             :             {
    1104       81098 :                 const int i = papsPoints[k]->i;
    1105       81098 :                 double dfRX = padfX[i] - dfXPoint;
    1106       81098 :                 double dfRY = padfY[i] - dfYPoint;
    1107             : 
    1108       81098 :                 if (bRotated)
    1109             :                 {
    1110       47527 :                     const double dfRXRotated =
    1111       47527 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1112       47527 :                     const double dfRYRotated =
    1113       47527 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1114       47527 :                     dfRX = dfRXRotated;
    1115       47527 :                     dfRY = dfRYRotated;
    1116             :                 }
    1117             : 
    1118       81098 :                 if (dfRadius2Square * dfRX * dfRX +
    1119       81098 :                         dfRadius1Square * dfRY * dfRY <=
    1120             :                     dfR12Square)
    1121             :                 {
    1122       32590 :                     if (dfMinimumValue > padfZ[i])
    1123        4455 :                         dfMinimumValue = padfZ[i];
    1124       32590 :                     n++;
    1125             :                 }
    1126             :             }
    1127             :         }
    1128        2002 :         CPLFree(papsPoints);
    1129             :     }
    1130             :     else
    1131             :     {
    1132      328080 :         GUInt32 i = 0;
    1133     2126480 :         while (i < nPoints)
    1134             :         {
    1135     1798400 :             double dfRX = padfX[i] - dfXPoint;
    1136     1798400 :             double dfRY = padfY[i] - dfYPoint;
    1137             : 
    1138     1798400 :             if (bRotated)
    1139             :             {
    1140           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1141           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1142             : 
    1143           0 :                 dfRX = dfRXRotated;
    1144           0 :                 dfRY = dfRYRotated;
    1145             :             }
    1146             : 
    1147             :             // Is this point located inside the search ellipse?
    1148     1798400 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
    1149             :                 dfR12Square)
    1150             :             {
    1151      773688 :                 if (dfMinimumValue > padfZ[i])
    1152      423220 :                     dfMinimumValue = padfZ[i];
    1153      773688 :                 n++;
    1154             :             }
    1155             : 
    1156     1798400 :             i++;
    1157             :         }
    1158             :     }
    1159             : 
    1160      330082 :     if (n < poOptions->nMinPoints || n == 0)
    1161             :     {
    1162       78136 :         *pdfValue = poOptions->dfNoDataValue;
    1163             :     }
    1164             :     else
    1165             :     {
    1166      251946 :         *pdfValue = dfMinimumValue;
    1167             :     }
    1168             : 
    1169      330082 :     return CE_None;
    1170             : }
    1171             : 
    1172             : /************************************************************************/
    1173             : /*           GDALGridDataMetricMinimumOrMaximumPerQuadrant()            */
    1174             : /************************************************************************/
    1175             : 
    1176             : /**
    1177             :  * Minimum or maximum data value (data metric), with a per-quadrant search
    1178             :  * logic.
    1179             :  */
    1180             : template <bool IS_MIN>
    1181      131086 : static CPLErr GDALGridDataMetricMinimumOrMaximumPerQuadrant(
    1182             :     const void *poOptionsIn, const double *padfX, const double *padfY,
    1183             :     const double *padfZ, double dfXPoint, double dfYPoint, double *pdfValue,
    1184             :     void *hExtraParamsIn)
    1185             : {
    1186      131086 :     const GDALGridDataMetricsOptions *const poOptions =
    1187             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1188             : 
    1189             :     // Pre-compute search ellipse parameters.
    1190      131086 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1191      131086 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1192      131086 :     const double dfSearchRadius =
    1193      131086 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1194      131086 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1195             : 
    1196             :     // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
    1197      131086 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
    1198      131086 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
    1199             : 
    1200             :     // Compute coefficients for coordinate system rotation.
    1201      131086 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1202      131086 :     const bool bRotated = dfAngle != 0.0;
    1203      131086 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1204      131086 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1205             : 
    1206      131086 :     GDALGridExtraParameters *psExtraParams =
    1207             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1208      131086 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1209      131086 :     CPLAssert(phQuadTree);
    1210             : 
    1211             :     CPLRectObj sAoi;
    1212      131086 :     sAoi.minx = dfXPoint - dfSearchRadius;
    1213      131086 :     sAoi.miny = dfYPoint - dfSearchRadius;
    1214      131086 :     sAoi.maxx = dfXPoint + dfSearchRadius;
    1215      131086 :     sAoi.maxy = dfYPoint + dfSearchRadius;
    1216      131086 :     int nFeatureCount = 0;
    1217             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1218      131086 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1219     1310860 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
    1220             : 
    1221      131086 :     if (nFeatureCount != 0)
    1222             :     {
    1223      548076 :         for (int k = 0; k < nFeatureCount; k++)
    1224             :         {
    1225      416990 :             const int i = papsPoints[k]->i;
    1226      416990 :             double dfRX = padfX[i] - dfXPoint;
    1227      416990 :             double dfRY = padfY[i] - dfYPoint;
    1228             : 
    1229      416990 :             if (bRotated)
    1230             :             {
    1231           8 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1232           8 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1233           8 :                 dfRX = dfRXRotated;
    1234           8 :                 dfRY = dfRYRotated;
    1235             :             }
    1236             : 
    1237      416990 :             const double dfRXSquare = dfRX * dfRX;
    1238      416990 :             const double dfRYSquare = dfRY * dfRY;
    1239             : 
    1240      416990 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
    1241             :                 dfR12Square)
    1242             :             {
    1243      399226 :                 const int iQuadrant =
    1244      399226 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
    1245      399226 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
    1246      798452 :                     std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
    1247             :             }
    1248             :         }
    1249             :     }
    1250      131086 :     CPLFree(papsPoints);
    1251             : 
    1252      655430 :     std::multimap<double, double>::iterator aoIter[] = {
    1253      131086 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
    1254      131086 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
    1255      131086 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
    1256      131086 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
    1257             :     };
    1258      131086 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
    1259             : 
    1260             :     // Examine all "neighbors" within the radius (sorted by distance via the
    1261             :     // multimap), and use the closest n points based on distance until the max
    1262             :     // is reached.
    1263             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
    1264             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
    1265             :     // point in quarant 0, etc.
    1266      131086 :     int nQuadrantIterFinishedFlag = 0;
    1267      131086 :     GUInt32 anPerQuadrant[4] = {0};
    1268      131086 :     double dfExtremum = IS_MIN ? std::numeric_limits<double>::max()
    1269             :                                : -std::numeric_limits<double>::max();
    1270      131086 :     GUInt32 n = 0;
    1271      964301 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
    1272             :     {
    1273      964301 :         if (aoIter[iQuadrant] ==
    1274     2256311 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
    1275      327710 :             (nMaxPointsPerQuadrant > 0 &&
    1276      327710 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
    1277             :         {
    1278      630613 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
    1279      630613 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
    1280      131086 :                 break;
    1281      499527 :             continue;
    1282             :         }
    1283             : 
    1284      333688 :         const double dfZ = aoIter[iQuadrant]->second;
    1285      333688 :         ++aoIter[iQuadrant];
    1286             : 
    1287             :         if (IS_MIN)
    1288             :         {
    1289      333667 :             if (dfExtremum > dfZ)
    1290      254398 :                 dfExtremum = dfZ;
    1291             :         }
    1292             :         else
    1293             :         {
    1294          21 :             if (dfExtremum < dfZ)
    1295          10 :                 dfExtremum = dfZ;
    1296             :         }
    1297      333688 :         n++;
    1298      333688 :         anPerQuadrant[iQuadrant]++;
    1299             :         /*if( nMaxPoints > 0 && n >= nMaxPoints )
    1300             :         {
    1301             :             break;
    1302             :         }*/
    1303             :     }
    1304             : 
    1305      131086 :     if (nMinPointsPerQuadrant > 0 &&
    1306       65546 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
    1307           8 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
    1308           6 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
    1309           6 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
    1310             :     {
    1311       65540 :         *pdfValue = poOptions->dfNoDataValue;
    1312             :     }
    1313       65546 :     else if (n < poOptions->nMinPoints || n == 0)
    1314             :     {
    1315           2 :         *pdfValue = poOptions->dfNoDataValue;
    1316             :     }
    1317             :     else
    1318             :     {
    1319       65544 :         *pdfValue = dfExtremum;
    1320             :     }
    1321             : 
    1322      262172 :     return CE_None;
    1323             : }
    1324             : 
    1325             : /************************************************************************/
    1326             : /*                GDALGridDataMetricMinimumPerQuadrant()                */
    1327             : /************************************************************************/
    1328             : 
    1329             : /**
    1330             :  * Minimum data value (data metric), with a per-quadrant search logic.
    1331             :  */
    1332      131079 : static CPLErr GDALGridDataMetricMinimumPerQuadrant(
    1333             :     const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
    1334             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
    1335             :     double *pdfValue, void *hExtraParamsIn)
    1336             : {
    1337      131079 :     return GDALGridDataMetricMinimumOrMaximumPerQuadrant</*IS_MIN=*/true>(
    1338             :         poOptionsIn, padfX, padfY, padfZ, dfXPoint, dfYPoint, pdfValue,
    1339      131079 :         hExtraParamsIn);
    1340             : }
    1341             : 
    1342             : /************************************************************************/
    1343             : /*                     GDALGridDataMetricMaximum()                      */
    1344             : /************************************************************************/
    1345             : 
    1346             : /**
    1347             :  * Maximum data value (data metric).
    1348             :  *
    1349             :  * Maximum value found in grid node search ellipse. If there are no points
    1350             :  * found, the specified NODATA value will be returned.
    1351             :  *
    1352             :  * \f[
    1353             :  *      Z=\max{(Z_1,Z_2,\ldots,Z_n)}
    1354             :  * \f]
    1355             :  *
    1356             :  *  where
    1357             :  *  <ul>
    1358             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    1359             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
    1360             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    1361             :  *  </ul>
    1362             :  *
    1363             :  * @param poOptionsIn Algorithm parameters. This should point to
    1364             :  * GDALGridDataMetricsOptions object.
    1365             :  * @param nPoints Number of elements in input arrays.
    1366             :  * @param padfX Input array of X coordinates.
    1367             :  * @param padfY Input array of Y coordinates.
    1368             :  * @param padfZ Input array of Z values.
    1369             :  * @param dfXPoint X coordinate of the point to compute.
    1370             :  * @param dfYPoint Y coordinate of the point to compute.
    1371             :  * @param pdfValue Pointer to variable where the computed grid node value
    1372             :  * will be returned.
    1373             :  * @param hExtraParamsIn extra parameters (unused)
    1374             :  *
    1375             :  * @return CE_None on success or CE_Failure if something goes wrong.
    1376             :  */
    1377             : 
    1378       67938 : CPLErr GDALGridDataMetricMaximum(const void *poOptionsIn, GUInt32 nPoints,
    1379             :                                  const double *padfX, const double *padfY,
    1380             :                                  const double *padfZ, double dfXPoint,
    1381             :                                  double dfYPoint, double *pdfValue,
    1382             :                                  void *hExtraParamsIn)
    1383             : {
    1384             :     // TODO: For optimization purposes pre-computed parameters should be moved
    1385             :     // out of this routine to the calling function.
    1386             : 
    1387       67938 :     const GDALGridDataMetricsOptions *const poOptions =
    1388             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1389             : 
    1390             :     // Pre-compute search ellipse parameters.
    1391       67938 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1392       67938 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1393             :     const double dfSearchRadius =
    1394       67938 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1395       67938 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1396             : 
    1397       67938 :     GDALGridExtraParameters *psExtraParams =
    1398             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1399       67938 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1400             : 
    1401             :     // Compute coefficients for coordinate system rotation.
    1402       67938 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1403       67938 :     const bool bRotated = dfAngle != 0.0;
    1404       67938 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1405       67938 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1406             : 
    1407       67938 :     double dfMaximumValue = -std::numeric_limits<double>::max();
    1408       67938 :     GUInt32 n = 0;
    1409       67938 :     if (phQuadTree != nullptr)
    1410             :     {
    1411             :         CPLRectObj sAoi;
    1412        2002 :         sAoi.minx = dfXPoint - dfSearchRadius;
    1413        2002 :         sAoi.miny = dfYPoint - dfSearchRadius;
    1414        2002 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    1415        2002 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    1416        2002 :         int nFeatureCount = 0;
    1417             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1418        2002 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1419        2002 :         if (nFeatureCount != 0)
    1420             :         {
    1421       38940 :             for (int k = 0; k < nFeatureCount; k++)
    1422             :             {
    1423       36938 :                 const int i = papsPoints[k]->i;
    1424       36938 :                 double dfRX = padfX[i] - dfXPoint;
    1425       36938 :                 double dfRY = padfY[i] - dfYPoint;
    1426             : 
    1427       36938 :                 if (bRotated)
    1428             :                 {
    1429         803 :                     const double dfRXRotated =
    1430         803 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1431         803 :                     const double dfRYRotated =
    1432         803 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1433         803 :                     dfRX = dfRXRotated;
    1434         803 :                     dfRY = dfRYRotated;
    1435             :                 }
    1436             : 
    1437       36938 :                 if (dfRadius2Square * dfRX * dfRX +
    1438       36938 :                         dfRadius1Square * dfRY * dfRY <=
    1439             :                     dfR12Square)
    1440             :                 {
    1441       24560 :                     if (dfMaximumValue < padfZ[i])
    1442        4140 :                         dfMaximumValue = padfZ[i];
    1443       24560 :                     n++;
    1444             :                 }
    1445             :             }
    1446             :         }
    1447        2002 :         CPLFree(papsPoints);
    1448             :     }
    1449             :     else
    1450             :     {
    1451       65936 :         GUInt32 i = 0;
    1452      553616 :         while (i < nPoints)
    1453             :         {
    1454      487680 :             double dfRX = padfX[i] - dfXPoint;
    1455      487680 :             double dfRY = padfY[i] - dfYPoint;
    1456             : 
    1457      487680 :             if (bRotated)
    1458             :             {
    1459           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1460           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1461             : 
    1462           0 :                 dfRX = dfRXRotated;
    1463           0 :                 dfRY = dfRYRotated;
    1464             :             }
    1465             : 
    1466             :             // Is this point located inside the search ellipse?
    1467      487680 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
    1468             :                 dfR12Square)
    1469             :             {
    1470      487680 :                 if (dfMaximumValue < padfZ[i])
    1471      200608 :                     dfMaximumValue = padfZ[i];
    1472      487680 :                 n++;
    1473             :             }
    1474             : 
    1475      487680 :             i++;
    1476             :         }
    1477             :     }
    1478             : 
    1479       67938 :     if (n < poOptions->nMinPoints || n == 0)
    1480             :     {
    1481           0 :         *pdfValue = poOptions->dfNoDataValue;
    1482             :     }
    1483             :     else
    1484             :     {
    1485       67938 :         *pdfValue = dfMaximumValue;
    1486             :     }
    1487             : 
    1488       67938 :     return CE_None;
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*                GDALGridDataMetricMaximumPerQuadrant()                */
    1493             : /************************************************************************/
    1494             : 
    1495             : /**
    1496             :  * Maximum data value (data metric), with a per-quadrant search logic.
    1497             :  */
    1498           7 : static CPLErr GDALGridDataMetricMaximumPerQuadrant(
    1499             :     const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
    1500             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
    1501             :     double *pdfValue, void *hExtraParamsIn)
    1502             : {
    1503           7 :     return GDALGridDataMetricMinimumOrMaximumPerQuadrant</*IS_MIN=*/false>(
    1504             :         poOptionsIn, padfX, padfY, padfZ, dfXPoint, dfYPoint, pdfValue,
    1505           7 :         hExtraParamsIn);
    1506             : }
    1507             : 
    1508             : /************************************************************************/
    1509             : /*                      GDALGridDataMetricRange()                       */
    1510             : /************************************************************************/
    1511             : 
    1512             : /**
    1513             :  * Data range (data metric).
    1514             :  *
    1515             :  * A difference between the minimum and maximum values found in grid node
    1516             :  * search ellipse. If there are no points found, the specified NODATA
    1517             :  * value will be returned.
    1518             :  *
    1519             :  * \f[
    1520             :  *      Z=\max{(Z_1,Z_2,\ldots,Z_n)}-\min{(Z_1,Z_2,\ldots,Z_n)}
    1521             :  * \f]
    1522             :  *
    1523             :  *  where
    1524             :  *  <ul>
    1525             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    1526             :  *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
    1527             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    1528             :  *  </ul>
    1529             :  *
    1530             :  * @param poOptionsIn Algorithm parameters. This should point to
    1531             :  * GDALGridDataMetricsOptions object.
    1532             :  * @param nPoints Number of elements in input arrays.
    1533             :  * @param padfX Input array of X coordinates.
    1534             :  * @param padfY Input array of Y coordinates.
    1535             :  * @param padfZ Input array of Z values.
    1536             :  * @param dfXPoint X coordinate of the point to compute.
    1537             :  * @param dfYPoint Y coordinate of the point to compute.
    1538             :  * @param pdfValue Pointer to variable where the computed grid node value
    1539             :  * will be returned.
    1540             :  * @param hExtraParamsIn extra parameters (unused)
    1541             :  *
    1542             :  * @return CE_None on success or CE_Failure if something goes wrong.
    1543             :  */
    1544             : 
    1545       66738 : CPLErr GDALGridDataMetricRange(const void *poOptionsIn, GUInt32 nPoints,
    1546             :                                const double *padfX, const double *padfY,
    1547             :                                const double *padfZ, double dfXPoint,
    1548             :                                double dfYPoint, double *pdfValue,
    1549             :                                void *hExtraParamsIn)
    1550             : {
    1551             :     // TODO: For optimization purposes pre-computed parameters should be moved
    1552             :     // out of this routine to the calling function.
    1553             : 
    1554       66738 :     const GDALGridDataMetricsOptions *const poOptions =
    1555             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1556             :     // Pre-compute search ellipse parameters.
    1557       66738 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1558       66738 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1559             :     const double dfSearchRadius =
    1560       66738 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1561       66738 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1562             : 
    1563       66738 :     GDALGridExtraParameters *psExtraParams =
    1564             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1565       66738 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1566             : 
    1567             :     // Compute coefficients for coordinate system rotation.
    1568       66738 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1569       66738 :     const bool bRotated = dfAngle != 0.0;
    1570       66738 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1571       66738 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1572             : 
    1573       66738 :     double dfMaximumValue = -std::numeric_limits<double>::max();
    1574       66738 :     double dfMinimumValue = std::numeric_limits<double>::max();
    1575       66738 :     GUInt32 n = 0;
    1576       66738 :     if (phQuadTree != nullptr)
    1577             :     {
    1578             :         CPLRectObj sAoi;
    1579         802 :         sAoi.minx = dfXPoint - dfSearchRadius;
    1580         802 :         sAoi.miny = dfYPoint - dfSearchRadius;
    1581         802 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    1582         802 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    1583         802 :         int nFeatureCount = 0;
    1584             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1585         802 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1586         802 :         if (nFeatureCount != 0)
    1587             :         {
    1588        7536 :             for (int k = 0; k < nFeatureCount; k++)
    1589             :             {
    1590        6734 :                 const int i = papsPoints[k]->i;
    1591        6734 :                 double dfRX = padfX[i] - dfXPoint;
    1592        6734 :                 double dfRY = padfY[i] - dfYPoint;
    1593             : 
    1594        6734 :                 if (bRotated)
    1595             :                 {
    1596           3 :                     const double dfRXRotated =
    1597           3 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1598           3 :                     const double dfRYRotated =
    1599           3 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1600           3 :                     dfRX = dfRXRotated;
    1601           3 :                     dfRY = dfRYRotated;
    1602             :                 }
    1603             : 
    1604        6734 :                 if (dfRadius2Square * dfRX * dfRX +
    1605        6734 :                         dfRadius1Square * dfRY * dfRY <=
    1606             :                     dfR12Square)
    1607             :                 {
    1608        6732 :                     if (dfMinimumValue > padfZ[i])
    1609        1952 :                         dfMinimumValue = padfZ[i];
    1610        6732 :                     if (dfMaximumValue < padfZ[i])
    1611        1838 :                         dfMaximumValue = padfZ[i];
    1612        6732 :                     n++;
    1613             :                 }
    1614             :             }
    1615             :         }
    1616         802 :         CPLFree(papsPoints);
    1617             :     }
    1618             :     else
    1619             :     {
    1620       65936 :         GUInt32 i = 0;
    1621      553616 :         while (i < nPoints)
    1622             :         {
    1623      487680 :             double dfRX = padfX[i] - dfXPoint;
    1624      487680 :             double dfRY = padfY[i] - dfYPoint;
    1625             : 
    1626      487680 :             if (bRotated)
    1627             :             {
    1628           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1629           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1630             : 
    1631           0 :                 dfRX = dfRXRotated;
    1632           0 :                 dfRY = dfRYRotated;
    1633             :             }
    1634             : 
    1635             :             // Is this point located inside the search ellipse?
    1636      487680 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
    1637             :                 dfR12Square)
    1638             :             {
    1639      487680 :                 if (dfMinimumValue > padfZ[i])
    1640      198208 :                     dfMinimumValue = padfZ[i];
    1641      487680 :                 if (dfMaximumValue < padfZ[i])
    1642      200608 :                     dfMaximumValue = padfZ[i];
    1643      487680 :                 n++;
    1644             :             }
    1645             : 
    1646      487680 :             i++;
    1647             :         }
    1648             :     }
    1649             : 
    1650       66738 :     if (n < poOptions->nMinPoints || n == 0)
    1651             :     {
    1652         152 :         *pdfValue = poOptions->dfNoDataValue;
    1653             :     }
    1654             :     else
    1655             :     {
    1656       66586 :         *pdfValue = dfMaximumValue - dfMinimumValue;
    1657             :     }
    1658             : 
    1659       66738 :     return CE_None;
    1660             : }
    1661             : 
    1662             : /************************************************************************/
    1663             : /*                 GDALGridDataMetricRangePerQuadrant()                 */
    1664             : /************************************************************************/
    1665             : 
    1666             : /**
    1667             :  * Data range (data metric), with a per-quadrant search logic.
    1668             :  */
    1669           7 : static CPLErr GDALGridDataMetricRangePerQuadrant(
    1670             :     const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
    1671             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
    1672             :     double *pdfValue, void *hExtraParamsIn)
    1673             : {
    1674           7 :     const GDALGridDataMetricsOptions *const poOptions =
    1675             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1676             : 
    1677             :     // Pre-compute search ellipse parameters.
    1678           7 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1679           7 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1680             :     const double dfSearchRadius =
    1681           7 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1682           7 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1683             : 
    1684             :     // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
    1685           7 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
    1686           7 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
    1687             : 
    1688             :     // Compute coefficients for coordinate system rotation.
    1689           7 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1690           7 :     const bool bRotated = dfAngle != 0.0;
    1691           7 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1692           7 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1693             : 
    1694           7 :     GDALGridExtraParameters *psExtraParams =
    1695             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1696           7 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1697           7 :     CPLAssert(phQuadTree);
    1698             : 
    1699             :     CPLRectObj sAoi;
    1700           7 :     sAoi.minx = dfXPoint - dfSearchRadius;
    1701           7 :     sAoi.miny = dfYPoint - dfSearchRadius;
    1702           7 :     sAoi.maxx = dfXPoint + dfSearchRadius;
    1703           7 :     sAoi.maxy = dfYPoint + dfSearchRadius;
    1704           7 :     int nFeatureCount = 0;
    1705             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1706           7 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1707          70 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
    1708             : 
    1709           7 :     if (nFeatureCount != 0)
    1710             :     {
    1711          36 :         for (int k = 0; k < nFeatureCount; k++)
    1712             :         {
    1713          29 :             const int i = papsPoints[k]->i;
    1714          29 :             double dfRX = padfX[i] - dfXPoint;
    1715          29 :             double dfRY = padfY[i] - dfYPoint;
    1716             : 
    1717          29 :             if (bRotated)
    1718             :             {
    1719           4 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1720           4 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1721           4 :                 dfRX = dfRXRotated;
    1722           4 :                 dfRY = dfRYRotated;
    1723             :             }
    1724             : 
    1725          29 :             const double dfRXSquare = dfRX * dfRX;
    1726          29 :             const double dfRYSquare = dfRY * dfRY;
    1727             : 
    1728          29 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
    1729             :                 dfR12Square)
    1730             :             {
    1731          22 :                 const int iQuadrant =
    1732          22 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
    1733             :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
    1734          22 :                     std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
    1735             :             }
    1736             :         }
    1737             :     }
    1738           7 :     CPLFree(papsPoints);
    1739             : 
    1740             :     std::multimap<double, double>::iterator aoIter[] = {
    1741           7 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
    1742           7 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
    1743           7 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
    1744           7 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
    1745           7 :     };
    1746           7 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
    1747             : 
    1748             :     // Examine all "neighbors" within the radius (sorted by distance via the
    1749             :     // multimap), and use the closest n points based on distance until the max
    1750             :     // is reached.
    1751             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
    1752             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
    1753             :     // point in quarant 0, etc.
    1754           7 :     int nQuadrantIterFinishedFlag = 0;
    1755           7 :     GUInt32 anPerQuadrant[4] = {0};
    1756           7 :     double dfMaximumValue = -std::numeric_limits<double>::max();
    1757           7 :     double dfMinimumValue = std::numeric_limits<double>::max();
    1758           7 :     GUInt32 n = 0;
    1759           7 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
    1760             :     {
    1761          68 :         if (aoIter[iQuadrant] ==
    1762         151 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
    1763          15 :             (nMaxPointsPerQuadrant > 0 &&
    1764          15 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
    1765             :         {
    1766          47 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
    1767          47 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
    1768           7 :                 break;
    1769          40 :             continue;
    1770             :         }
    1771             : 
    1772          21 :         const double dfZ = aoIter[iQuadrant]->second;
    1773          21 :         ++aoIter[iQuadrant];
    1774             : 
    1775          21 :         if (dfMinimumValue > dfZ)
    1776          10 :             dfMinimumValue = dfZ;
    1777          21 :         if (dfMaximumValue < dfZ)
    1778          10 :             dfMaximumValue = dfZ;
    1779          21 :         n++;
    1780          21 :         anPerQuadrant[iQuadrant]++;
    1781             :         /*if( nMaxPoints > 0 && n >= nMaxPoints )
    1782             :         {
    1783             :             break;
    1784             :         }*/
    1785          61 :     }
    1786             : 
    1787           7 :     if (nMinPointsPerQuadrant > 0 &&
    1788           5 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
    1789           4 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
    1790           3 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
    1791           3 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
    1792             :     {
    1793           2 :         *pdfValue = poOptions->dfNoDataValue;
    1794             :     }
    1795           5 :     else if (n < poOptions->nMinPoints || n == 0)
    1796             :     {
    1797           1 :         *pdfValue = poOptions->dfNoDataValue;
    1798             :     }
    1799             :     else
    1800             :     {
    1801           4 :         *pdfValue = dfMaximumValue - dfMinimumValue;
    1802             :     }
    1803             : 
    1804          14 :     return CE_None;
    1805             : }
    1806             : 
    1807             : /************************************************************************/
    1808             : /*                      GDALGridDataMetricCount()                       */
    1809             : /************************************************************************/
    1810             : 
    1811             : /**
    1812             :  * Number of data points (data metric).
    1813             :  *
    1814             :  * A number of data points found in grid node search ellipse.
    1815             :  *
    1816             :  * \f[
    1817             :  *      Z=n
    1818             :  * \f]
    1819             :  *
    1820             :  *  where
    1821             :  *  <ul>
    1822             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    1823             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    1824             :  *  </ul>
    1825             :  *
    1826             :  * @param poOptionsIn Algorithm parameters. This should point to
    1827             :  * GDALGridDataMetricsOptions object.
    1828             :  * @param nPoints Number of elements in input arrays.
    1829             :  * @param padfX Input array of X coordinates.
    1830             :  * @param padfY Input array of Y coordinates.
    1831             :  * @param padfZ Input array of Z values.
    1832             :  * @param dfXPoint X coordinate of the point to compute.
    1833             :  * @param dfYPoint Y coordinate of the point to compute.
    1834             :  * @param pdfValue Pointer to variable where the computed grid node value
    1835             :  * will be returned.
    1836             :  * @param hExtraParamsIn extra parameters (unused)
    1837             :  *
    1838             :  * @return CE_None on success or CE_Failure if something goes wrong.
    1839             :  */
    1840             : 
    1841       67538 : CPLErr GDALGridDataMetricCount(const void *poOptionsIn, GUInt32 nPoints,
    1842             :                                const double *padfX, const double *padfY,
    1843             :                                CPL_UNUSED const double *padfZ, double dfXPoint,
    1844             :                                double dfYPoint, double *pdfValue,
    1845             :                                void *hExtraParamsIn)
    1846             : {
    1847             :     // TODO: For optimization purposes pre-computed parameters should be moved
    1848             :     // out of this routine to the calling function.
    1849             : 
    1850       67538 :     const GDALGridDataMetricsOptions *const poOptions =
    1851             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1852             : 
    1853             :     // Pre-compute search ellipse parameters.
    1854       67538 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1855       67538 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1856             :     const double dfSearchRadius =
    1857       67538 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1858       67538 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1859             : 
    1860       67538 :     GDALGridExtraParameters *psExtraParams =
    1861             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1862       67538 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1863             : 
    1864             :     // Compute coefficients for coordinate system rotation.
    1865       67538 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1866       67538 :     const bool bRotated = dfAngle != 0.0;
    1867       67538 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1868       67538 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1869             : 
    1870       67538 :     GUInt32 n = 0;
    1871       67538 :     if (phQuadTree != nullptr)
    1872             :     {
    1873             :         CPLRectObj sAoi;
    1874        2002 :         sAoi.minx = dfXPoint - dfSearchRadius;
    1875        2002 :         sAoi.miny = dfYPoint - dfSearchRadius;
    1876        2002 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    1877        2002 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    1878        2002 :         int nFeatureCount = 0;
    1879             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1880        2002 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1881        2002 :         if (nFeatureCount != 0)
    1882             :         {
    1883       84300 :             for (int k = 0; k < nFeatureCount; k++)
    1884             :             {
    1885       82298 :                 const int i = papsPoints[k]->i;
    1886       82298 :                 double dfRX = padfX[i] - dfXPoint;
    1887       82298 :                 double dfRY = padfY[i] - dfYPoint;
    1888             : 
    1889       82298 :                 if (bRotated)
    1890             :                 {
    1891           3 :                     const double dfRXRotated =
    1892           3 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1893           3 :                     const double dfRYRotated =
    1894           3 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1895           3 :                     dfRX = dfRXRotated;
    1896           3 :                     dfRY = dfRYRotated;
    1897             :                 }
    1898             : 
    1899       82298 :                 if (dfRadius2Square * dfRX * dfRX +
    1900       82298 :                         dfRadius1Square * dfRY * dfRY <=
    1901             :                     dfR12Square)
    1902             :                 {
    1903       57316 :                     n++;
    1904             :                 }
    1905             :             }
    1906             :         }
    1907        2002 :         CPLFree(papsPoints);
    1908             :     }
    1909             :     else
    1910             :     {
    1911       65536 :         GUInt32 i = 0;
    1912      393216 :         while (i < nPoints)
    1913             :         {
    1914      327680 :             double dfRX = padfX[i] - dfXPoint;
    1915      327680 :             double dfRY = padfY[i] - dfYPoint;
    1916             : 
    1917      327680 :             if (bRotated)
    1918             :             {
    1919           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    1920           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    1921             : 
    1922           0 :                 dfRX = dfRXRotated;
    1923           0 :                 dfRY = dfRYRotated;
    1924             :             }
    1925             : 
    1926             :             // Is this point located inside the search ellipse?
    1927      327680 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
    1928             :                 dfR12Square)
    1929             :             {
    1930       71502 :                 n++;
    1931             :             }
    1932             : 
    1933      327680 :             i++;
    1934             :         }
    1935             :     }
    1936             : 
    1937       67538 :     if (n < poOptions->nMinPoints)
    1938             :     {
    1939           0 :         *pdfValue = poOptions->dfNoDataValue;
    1940             :     }
    1941             :     else
    1942             :     {
    1943       67538 :         *pdfValue = static_cast<double>(n);
    1944             :     }
    1945             : 
    1946       67538 :     return CE_None;
    1947             : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*                 GDALGridDataMetricCountPerQuadrant()                 */
    1951             : /************************************************************************/
    1952             : 
    1953             : /**
    1954             :  * Number of data points (data metric), with a per-quadrant search logic.
    1955             :  */
    1956           7 : static CPLErr GDALGridDataMetricCountPerQuadrant(
    1957             :     const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
    1958             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
    1959             :     double *pdfValue, void *hExtraParamsIn)
    1960             : {
    1961           7 :     const GDALGridDataMetricsOptions *const poOptions =
    1962             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    1963             : 
    1964             :     // Pre-compute search ellipse parameters.
    1965           7 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    1966           7 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    1967             :     const double dfSearchRadius =
    1968           7 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    1969           7 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    1970             : 
    1971             :     // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
    1972           7 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
    1973           7 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
    1974             : 
    1975             :     // Compute coefficients for coordinate system rotation.
    1976           7 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    1977           7 :     const bool bRotated = dfAngle != 0.0;
    1978           7 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    1979           7 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    1980             : 
    1981           7 :     GDALGridExtraParameters *psExtraParams =
    1982             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    1983           7 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    1984           7 :     CPLAssert(phQuadTree);
    1985             : 
    1986             :     CPLRectObj sAoi;
    1987           7 :     sAoi.minx = dfXPoint - dfSearchRadius;
    1988           7 :     sAoi.miny = dfYPoint - dfSearchRadius;
    1989           7 :     sAoi.maxx = dfXPoint + dfSearchRadius;
    1990           7 :     sAoi.maxy = dfYPoint + dfSearchRadius;
    1991           7 :     int nFeatureCount = 0;
    1992             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    1993           7 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    1994          70 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
    1995             : 
    1996           7 :     if (nFeatureCount != 0)
    1997             :     {
    1998          36 :         for (int k = 0; k < nFeatureCount; k++)
    1999             :         {
    2000          29 :             const int i = papsPoints[k]->i;
    2001          29 :             double dfRX = padfX[i] - dfXPoint;
    2002          29 :             double dfRY = padfY[i] - dfYPoint;
    2003             : 
    2004          29 :             if (bRotated)
    2005             :             {
    2006           4 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    2007           4 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    2008           4 :                 dfRX = dfRXRotated;
    2009           4 :                 dfRY = dfRYRotated;
    2010             :             }
    2011             : 
    2012          29 :             const double dfRXSquare = dfRX * dfRX;
    2013          29 :             const double dfRYSquare = dfRY * dfRY;
    2014             : 
    2015          29 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
    2016             :                 dfR12Square)
    2017             :             {
    2018          22 :                 const int iQuadrant =
    2019          22 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
    2020             :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
    2021          22 :                     std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
    2022             :             }
    2023             :         }
    2024             :     }
    2025           7 :     CPLFree(papsPoints);
    2026             : 
    2027             :     std::multimap<double, double>::iterator aoIter[] = {
    2028           7 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
    2029           7 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
    2030           7 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
    2031           7 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
    2032           7 :     };
    2033           7 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
    2034             : 
    2035             :     // Examine all "neighbors" within the radius (sorted by distance via the
    2036             :     // multimap), and use the closest n points based on distance until the max
    2037             :     // is reached.
    2038             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
    2039             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
    2040             :     // point in quarant 0, etc.
    2041           7 :     int nQuadrantIterFinishedFlag = 0;
    2042           7 :     GUInt32 anPerQuadrant[4] = {0};
    2043           7 :     GUInt32 n = 0;
    2044          68 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
    2045             :     {
    2046          68 :         if (aoIter[iQuadrant] ==
    2047         151 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
    2048          15 :             (nMaxPointsPerQuadrant > 0 &&
    2049          15 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
    2050             :         {
    2051          47 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
    2052          47 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
    2053           7 :                 break;
    2054          40 :             continue;
    2055             :         }
    2056             : 
    2057          21 :         ++aoIter[iQuadrant];
    2058             : 
    2059          21 :         n++;
    2060          21 :         anPerQuadrant[iQuadrant]++;
    2061             :         /*if( nMaxPoints > 0 && n >= nMaxPoints )
    2062             :         {
    2063             :             break;
    2064             :         }*/
    2065             :     }
    2066             : 
    2067           7 :     if (nMinPointsPerQuadrant > 0 &&
    2068           5 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
    2069           4 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
    2070           3 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
    2071           3 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
    2072             :     {
    2073           2 :         *pdfValue = poOptions->dfNoDataValue;
    2074             :     }
    2075           5 :     else if (n < poOptions->nMinPoints)
    2076             :     {
    2077           1 :         *pdfValue = poOptions->dfNoDataValue;
    2078             :     }
    2079             :     else
    2080             :     {
    2081           4 :         *pdfValue = static_cast<double>(n);
    2082             :     }
    2083             : 
    2084          14 :     return CE_None;
    2085             : }
    2086             : 
    2087             : /************************************************************************/
    2088             : /*                 GDALGridDataMetricAverageDistance()                  */
    2089             : /************************************************************************/
    2090             : 
    2091             : /**
    2092             :  * Average distance (data metric).
    2093             :  *
    2094             :  * An average distance between the grid node (center of the search ellipse)
    2095             :  * and all of the data points found in grid node search ellipse. If there are
    2096             :  * no points found, the specified NODATA value will be returned.
    2097             :  *
    2098             :  * \f[
    2099             :  *      Z=\frac{\sum_{i = 1}^n r_i}{n}
    2100             :  * \f]
    2101             :  *
    2102             :  *  where
    2103             :  *  <ul>
    2104             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    2105             :  *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
    2106             :  *           to point \f$i\f$,
    2107             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    2108             :  *  </ul>
    2109             :  *
    2110             :  * @param poOptionsIn Algorithm parameters. This should point to
    2111             :  * GDALGridDataMetricsOptions object.
    2112             :  * @param nPoints Number of elements in input arrays.
    2113             :  * @param padfX Input array of X coordinates.
    2114             :  * @param padfY Input array of Y coordinates.
    2115             :  * @param padfZ Input array of Z values (unused)
    2116             :  * @param dfXPoint X coordinate of the point to compute.
    2117             :  * @param dfYPoint Y coordinate of the point to compute.
    2118             :  * @param pdfValue Pointer to variable where the computed grid node value
    2119             :  * will be returned.
    2120             :  * @param hExtraParamsIn extra parameters (unused)
    2121             :  *
    2122             :  * @return CE_None on success or CE_Failure if something goes wrong.
    2123             :  */
    2124             : 
    2125       66738 : CPLErr GDALGridDataMetricAverageDistance(const void *poOptionsIn,
    2126             :                                          GUInt32 nPoints, const double *padfX,
    2127             :                                          const double *padfY,
    2128             :                                          CPL_UNUSED const double *padfZ,
    2129             :                                          double dfXPoint, double dfYPoint,
    2130             :                                          double *pdfValue, void *hExtraParamsIn)
    2131             : {
    2132             :     // TODO: For optimization purposes pre-computed parameters should be moved
    2133             :     // out of this routine to the calling function.
    2134             : 
    2135       66738 :     const GDALGridDataMetricsOptions *const poOptions =
    2136             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    2137             : 
    2138             :     // Pre-compute search ellipse parameters.
    2139       66738 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    2140       66738 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    2141             :     const double dfSearchRadius =
    2142       66738 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    2143       66738 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    2144             : 
    2145       66738 :     GDALGridExtraParameters *psExtraParams =
    2146             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    2147       66738 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    2148             : 
    2149             :     // Compute coefficients for coordinate system rotation.
    2150       66738 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    2151       66738 :     const bool bRotated = dfAngle != 0.0;
    2152       66738 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    2153       66738 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    2154             : 
    2155       66738 :     double dfAccumulator = 0.0;
    2156       66738 :     GUInt32 n = 0;
    2157       66738 :     if (phQuadTree != nullptr)
    2158             :     {
    2159             :         CPLRectObj sAoi;
    2160         802 :         sAoi.minx = dfXPoint - dfSearchRadius;
    2161         802 :         sAoi.miny = dfYPoint - dfSearchRadius;
    2162         802 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    2163         802 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    2164         802 :         int nFeatureCount = 0;
    2165             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    2166         802 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    2167         802 :         if (nFeatureCount != 0)
    2168             :         {
    2169       18480 :             for (int k = 0; k < nFeatureCount; k++)
    2170             :             {
    2171       17678 :                 const int i = papsPoints[k]->i;
    2172       17678 :                 double dfRX = padfX[i] - dfXPoint;
    2173       17678 :                 double dfRY = padfY[i] - dfYPoint;
    2174             : 
    2175       17678 :                 if (bRotated)
    2176             :                 {
    2177           3 :                     const double dfRXRotated =
    2178           3 :                         dfRX * dfCoeff1 + dfRY * dfCoeff2;
    2179           3 :                     const double dfRYRotated =
    2180           3 :                         dfRY * dfCoeff1 - dfRX * dfCoeff2;
    2181           3 :                     dfRX = dfRXRotated;
    2182           3 :                     dfRY = dfRYRotated;
    2183             :                 }
    2184             : 
    2185       17678 :                 if (dfRadius2Square * dfRX * dfRX +
    2186       17678 :                         dfRadius1Square * dfRY * dfRY <=
    2187             :                     dfR12Square)
    2188             :                 {
    2189       15084 :                     dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
    2190       15084 :                     n++;
    2191             :                 }
    2192             :             }
    2193             :         }
    2194         802 :         CPLFree(papsPoints);
    2195             :     }
    2196             :     else
    2197             :     {
    2198       65936 :         GUInt32 i = 0;
    2199             : 
    2200      553616 :         while (i < nPoints)
    2201             :         {
    2202      487680 :             double dfRX = padfX[i] - dfXPoint;
    2203      487680 :             double dfRY = padfY[i] - dfYPoint;
    2204             : 
    2205      487680 :             if (bRotated)
    2206             :             {
    2207           0 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    2208           0 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    2209             : 
    2210           0 :                 dfRX = dfRXRotated;
    2211           0 :                 dfRY = dfRYRotated;
    2212             :             }
    2213             : 
    2214             :             // Is this point located inside the search ellipse?
    2215      487680 :             if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
    2216             :                 dfR12Square)
    2217             :             {
    2218      487680 :                 dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
    2219      487680 :                 n++;
    2220             :             }
    2221             : 
    2222      487680 :             i++;
    2223             :         }
    2224             :     }
    2225             : 
    2226       66738 :     if (n < poOptions->nMinPoints || n == 0)
    2227             :     {
    2228           0 :         *pdfValue = poOptions->dfNoDataValue;
    2229             :     }
    2230             :     else
    2231             :     {
    2232       66738 :         *pdfValue = dfAccumulator / n;
    2233             :     }
    2234             : 
    2235       66738 :     return CE_None;
    2236             : }
    2237             : 
    2238             : /************************************************************************/
    2239             : /*            GDALGridDataMetricAverageDistancePerQuadrant()            */
    2240             : /************************************************************************/
    2241             : 
    2242             : /**
    2243             :  * Average distance (data metric), with a per-quadrant search logic.
    2244             :  */
    2245           7 : static CPLErr GDALGridDataMetricAverageDistancePerQuadrant(
    2246             :     const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
    2247             :     const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
    2248             :     double *pdfValue, void *hExtraParamsIn)
    2249             : {
    2250           7 :     const GDALGridDataMetricsOptions *const poOptions =
    2251             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    2252             : 
    2253             :     // Pre-compute search ellipse parameters.
    2254           7 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    2255           7 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    2256             :     const double dfSearchRadius =
    2257           7 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    2258           7 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    2259             : 
    2260             :     // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
    2261           7 :     const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
    2262           7 :     const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
    2263             : 
    2264             :     // Compute coefficients for coordinate system rotation.
    2265           7 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    2266           7 :     const bool bRotated = dfAngle != 0.0;
    2267           7 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    2268           7 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    2269             : 
    2270           7 :     GDALGridExtraParameters *psExtraParams =
    2271             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    2272           7 :     const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    2273           7 :     CPLAssert(phQuadTree);
    2274             : 
    2275             :     CPLRectObj sAoi;
    2276           7 :     sAoi.minx = dfXPoint - dfSearchRadius;
    2277           7 :     sAoi.miny = dfYPoint - dfSearchRadius;
    2278           7 :     sAoi.maxx = dfXPoint + dfSearchRadius;
    2279           7 :     sAoi.maxy = dfYPoint + dfSearchRadius;
    2280           7 :     int nFeatureCount = 0;
    2281             :     GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    2282           7 :         CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    2283          70 :     std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
    2284             : 
    2285           7 :     if (nFeatureCount != 0)
    2286             :     {
    2287          36 :         for (int k = 0; k < nFeatureCount; k++)
    2288             :         {
    2289          29 :             const int i = papsPoints[k]->i;
    2290          29 :             double dfRX = padfX[i] - dfXPoint;
    2291          29 :             double dfRY = padfY[i] - dfYPoint;
    2292             : 
    2293          29 :             if (bRotated)
    2294             :             {
    2295           4 :                 const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
    2296           4 :                 const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
    2297           4 :                 dfRX = dfRXRotated;
    2298           4 :                 dfRY = dfRYRotated;
    2299             :             }
    2300             : 
    2301          29 :             const double dfRXSquare = dfRX * dfRX;
    2302          29 :             const double dfRYSquare = dfRY * dfRY;
    2303             : 
    2304          29 :             if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
    2305             :                 dfR12Square)
    2306             :             {
    2307          22 :                 const int iQuadrant =
    2308          22 :                     ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
    2309             :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
    2310          22 :                     std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
    2311             :             }
    2312             :         }
    2313             :     }
    2314           7 :     CPLFree(papsPoints);
    2315             : 
    2316             :     std::multimap<double, double>::iterator aoIter[] = {
    2317           7 :         oMapDistanceToZValuesPerQuadrant[0].begin(),
    2318           7 :         oMapDistanceToZValuesPerQuadrant[1].begin(),
    2319           7 :         oMapDistanceToZValuesPerQuadrant[2].begin(),
    2320           7 :         oMapDistanceToZValuesPerQuadrant[3].begin(),
    2321           7 :     };
    2322           7 :     constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
    2323             : 
    2324             :     // Examine all "neighbors" within the radius (sorted by distance via the
    2325             :     // multimap), and use the closest n points based on distance until the max
    2326             :     // is reached.
    2327             :     // Do that by fetching the nearest point in quadrant 0, then the nearest
    2328             :     // point in quadrant 1, 2 and 3, and starting again with the next nearest
    2329             :     // point in quarant 0, etc.
    2330           7 :     int nQuadrantIterFinishedFlag = 0;
    2331           7 :     GUInt32 anPerQuadrant[4] = {0};
    2332           7 :     GUInt32 n = 0;
    2333           7 :     double dfAccumulator = 0;
    2334          68 :     for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
    2335             :     {
    2336          68 :         if (aoIter[iQuadrant] ==
    2337         151 :                 oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
    2338          15 :             (nMaxPointsPerQuadrant > 0 &&
    2339          15 :              anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
    2340             :         {
    2341          47 :             nQuadrantIterFinishedFlag |= 1 << iQuadrant;
    2342          47 :             if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
    2343           7 :                 break;
    2344          40 :             continue;
    2345             :         }
    2346             : 
    2347          21 :         dfAccumulator += sqrt(aoIter[iQuadrant]->first);
    2348          21 :         ++aoIter[iQuadrant];
    2349             : 
    2350          21 :         n++;
    2351          21 :         anPerQuadrant[iQuadrant]++;
    2352             :         /*if( nMaxPoints > 0 && n >= nMaxPoints )
    2353             :         {
    2354             :             break;
    2355             :         }*/
    2356             :     }
    2357             : 
    2358           7 :     if (nMinPointsPerQuadrant > 0 &&
    2359           5 :         (anPerQuadrant[0] < nMinPointsPerQuadrant ||
    2360           4 :          anPerQuadrant[1] < nMinPointsPerQuadrant ||
    2361           3 :          anPerQuadrant[2] < nMinPointsPerQuadrant ||
    2362           3 :          anPerQuadrant[3] < nMinPointsPerQuadrant))
    2363             :     {
    2364           2 :         *pdfValue = poOptions->dfNoDataValue;
    2365             :     }
    2366           5 :     else if (n < poOptions->nMinPoints || n == 0)
    2367             :     {
    2368           1 :         *pdfValue = poOptions->dfNoDataValue;
    2369             :     }
    2370             :     else
    2371             :     {
    2372           4 :         *pdfValue = dfAccumulator / n;
    2373             :     }
    2374             : 
    2375          14 :     return CE_None;
    2376             : }
    2377             : 
    2378             : /************************************************************************/
    2379             : /*                GDALGridDataMetricAverageDistancePts()                */
    2380             : /************************************************************************/
    2381             : 
    2382             : /**
    2383             :  * Average distance between points (data metric).
    2384             :  *
    2385             :  * An average distance between the data points found in grid node search
    2386             :  * ellipse. The distance between each pair of points within ellipse is
    2387             :  * calculated and average of all distances is set as a grid node value. If
    2388             :  * there are no points found, the specified NODATA value will be returned.
    2389             : 
    2390             :  *
    2391             :  * \f[
    2392             :  *      Z=\frac{\sum_{i = 1}^{n-1}\sum_{j=i+1}^{n}
    2393             :  r_{ij}}{\left(n-1\right)\,n-\frac{n+{\left(n-1\right)}^{2}-1}{2}}
    2394             :  * \f]
    2395             :  *
    2396             :  *  where
    2397             :  *  <ul>
    2398             :  *      <li> \f$Z\f$ is a resulting value at the grid node,
    2399             :  *      <li> \f$r_{ij}\f$ is an Euclidean distance between points
    2400             :  *           \f$i\f$ and \f$j\f$,
    2401             :  *      <li> \f$n\f$ is a total number of points in search ellipse.
    2402             :  *  </ul>
    2403             :  *
    2404             :  * @param poOptionsIn Algorithm parameters. This should point to
    2405             :  * GDALGridDataMetricsOptions object.
    2406             :  * @param nPoints Number of elements in input arrays.
    2407             :  * @param padfX Input array of X coordinates.
    2408             :  * @param padfY Input array of Y coordinates.
    2409             :  * @param padfZ Input array of Z values (unused)
    2410             :  * @param dfXPoint X coordinate of the point to compute.
    2411             :  * @param dfYPoint Y coordinate of the point to compute.
    2412             :  * @param pdfValue Pointer to variable where the computed grid node value
    2413             :  * will be returned.
    2414             :  * @param hExtraParamsIn extra parameters (unused)
    2415             :  *
    2416             :  * @return CE_None on success or CE_Failure if something goes wrong.
    2417             :  */
    2418             : 
    2419       66736 : CPLErr GDALGridDataMetricAverageDistancePts(
    2420             :     const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
    2421             :     const double *padfY, CPL_UNUSED const double *padfZ, double dfXPoint,
    2422             :     double dfYPoint, double *pdfValue, void *hExtraParamsIn)
    2423             : {
    2424             :     // TODO: For optimization purposes pre-computed parameters should be moved
    2425             :     // out of this routine to the calling function.
    2426             : 
    2427       66736 :     const GDALGridDataMetricsOptions *const poOptions =
    2428             :         static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
    2429             :     // Pre-compute search ellipse parameters.
    2430       66736 :     const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
    2431       66736 :     const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
    2432             :     const double dfSearchRadius =
    2433       66736 :         std::max(poOptions->dfRadius1, poOptions->dfRadius2);
    2434       66736 :     const double dfR12Square = dfRadius1Square * dfRadius2Square;
    2435             : 
    2436       66736 :     GDALGridExtraParameters *psExtraParams =
    2437             :         static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
    2438       66736 :     CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
    2439             : 
    2440             :     // Compute coefficients for coordinate system rotation.
    2441       66736 :     const double dfAngle = TO_RADIANS * poOptions->dfAngle;
    2442       66736 :     const bool bRotated = dfAngle != 0.0;
    2443       66736 :     const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
    2444       66736 :     const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
    2445             : 
    2446       66736 :     double dfAccumulator = 0.0;
    2447       66736 :     GUInt32 n = 0;
    2448       66736 :     if (phQuadTree != nullptr)
    2449             :     {
    2450             :         CPLRectObj sAoi;
    2451         800 :         sAoi.minx = dfXPoint - dfSearchRadius;
    2452         800 :         sAoi.miny = dfYPoint - dfSearchRadius;
    2453         800 :         sAoi.maxx = dfXPoint + dfSearchRadius;
    2454         800 :         sAoi.maxy = dfYPoint + dfSearchRadius;
    2455         800 :         int nFeatureCount = 0;
    2456             :         GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
    2457         800 :             CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
    2458         800 :         if (nFeatureCount != 0)
    2459             :         {
    2460       17672 :             for (int k = 0; k < nFeatureCount - 1; k++)
    2461             :             {
    2462       16872 :                 const int i = papsPoints[k]->i;
    2463       16872 :                 const double dfRX1 = padfX[i] - dfXPoint;
    2464       16872 :                 const double dfRY1 = padfY[i] - dfYPoint;
    2465             : 
    2466       16872 :                 if (dfRadius2Square * dfRX1 * dfRX1 +
    2467       16872 :                         dfRadius1Square * dfRY1 * dfRY1 <=
    2468             :                     dfR12Square)
    2469             :                 {
    2470      193952 :                     for (int j = k; j < nFeatureCount; j++)
    2471             :                     // Search all the remaining points within the ellipse and
    2472             :                     // compute distances between them and the first point.
    2473             :                     {
    2474      179318 :                         const int ji = papsPoints[j]->i;
    2475      179318 :                         double dfRX2 = padfX[ji] - dfXPoint;
    2476      179318 :                         double dfRY2 = padfY[ji] - dfYPoint;
    2477             : 
    2478      179318 :                         if (dfRadius2Square * dfRX2 * dfRX2 +
    2479      179318 :                                 dfRadius1Square * dfRY2 * dfRY2 <=
    2480             :                             dfR12Square)
    2481             :                         {
    2482      153666 :                             const double dfRX = padfX[ji] - padfX[i];
    2483      153666 :                             const double dfRY = padfY[ji] - padfY[i];
    2484             : 
    2485      153666 :                             dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
    2486      153666 :                             n++;
    2487             :                         }
    2488             :                     }
    2489             :                 }
    2490             :             }
    2491             :         }
    2492         800 :         CPLFree(papsPoints);
    2493             :     }
    2494             :     else
    2495             :     {
    2496       65936 :         GUInt32 i = 0;
    2497      487680 :         while (i < nPoints - 1)
    2498             :         {
    2499      421744 :             double dfRX1 = padfX[i] - dfXPoint;
    2500      421744 :             double dfRY1 = padfY[i] - dfYPoint;
    2501             : 
    2502      421744 :             if (bRotated)
    2503             :             {
    2504      159600 :                 const double dfRXRotated = dfRX1 * dfCoeff1 + dfRY1 * dfCoeff2;
    2505      159600 :                 const double dfRYRotated = dfRY1 * dfCoeff1 - dfRX1 * dfCoeff2;
    2506             : 
    2507      159600 :                 dfRX1 = dfRXRotated;
    2508      159600 :                 dfRY1 = dfRYRotated;
    2509             :             }
    2510             : 
    2511             :             // Is this point located inside the search ellipse?
    2512      421744 :             if (dfRadius2Square * dfRX1 * dfRX1 +
    2513      421744 :                     dfRadius1Square * dfRY1 * dfRY1 <=
    2514             :                 dfR12Square)
    2515             :             {
    2516             :                 // Search all the remaining points within the ellipse and
    2517             :                 // compute distances between them and the first point.
    2518     1439200 :                 for (GUInt32 j = i + 1; j < nPoints; j++)
    2519             :                 {
    2520     1174460 :                     double dfRX2 = padfX[j] - dfXPoint;
    2521     1174460 :                     double dfRY2 = padfY[j] - dfYPoint;
    2522             : 
    2523     1174460 :                     if (bRotated)
    2524             :                     {
    2525      519099 :                         const double dfRXRotated =
    2526      519099 :                             dfRX2 * dfCoeff1 + dfRY2 * dfCoeff2;
    2527      519099 :                         const double dfRYRotated =
    2528      519099 :                             dfRY2 * dfCoeff1 - dfRX2 * dfCoeff2;
    2529             : 
    2530      519099 :                         dfRX2 = dfRXRotated;
    2531      519099 :                         dfRY2 = dfRYRotated;
    2532             :                     }
    2533             : 
    2534     1174460 :                     if (dfRadius2Square * dfRX2 * dfRX2 +
    2535     1174460 :                             dfRadius1Square * dfRY2 * dfRY2 <=
    2536             :                         dfR12Square)
    2537             :                     {
    2538      662702 :                         const double dfRX = padfX[j] - padfX[i];
    2539      662702 :                         const double dfRY = padfY[j] - padfY[i];
    2540             : 
    2541      662702 :                         dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
    2542      662702 :                         n++;
    2543             :                     }
    2544             :                 }
    2545             :             }
    2546             : 
    2547      421744 :             i++;
    2548             :         }
    2549             :     }
    2550             : 
    2551             :     // Search for the first point within the search ellipse.
    2552       66736 :     if (n < poOptions->nMinPoints || n == 0)
    2553             :     {
    2554           0 :         *pdfValue = poOptions->dfNoDataValue;
    2555             :     }
    2556             :     else
    2557             :     {
    2558       66736 :         *pdfValue = dfAccumulator / n;
    2559             :     }
    2560             : 
    2561       66736 :     return CE_None;
    2562             : }
    2563             : 
    2564             : /************************************************************************/
    2565             : /*                           GDALGridLinear()                           */
    2566             : /************************************************************************/
    2567             : 
    2568             : /**
    2569             :  * Linear interpolation
    2570             :  *
    2571             :  * The Linear method performs linear interpolation by finding in which triangle
    2572             :  * of a Delaunay triangulation the point is, and by doing interpolation from
    2573             :  * its barycentric coordinates within the triangle.
    2574             :  * If the point is not in any triangle, depending on the radius, the
    2575             :  * algorithm will use the value of the nearest point (radius != 0),
    2576             :  * or the nodata value (radius == 0)
    2577             :  *
    2578             :  * @param poOptionsIn Algorithm parameters. This should point to
    2579             :  * GDALGridLinearOptions object.
    2580             :  * @param nPoints Number of elements in input arrays.
    2581             :  * @param padfX Input array of X coordinates.
    2582             :  * @param padfY Input array of Y coordinates.
    2583             :  * @param padfZ Input array of Z values.
    2584             :  * @param dfXPoint X coordinate of the point to compute.
    2585             :  * @param dfYPoint Y coordinate of the point to compute.
    2586             :  * @param pdfValue Pointer to variable where the computed grid node value
    2587             :  * will be returned.
    2588             :  * @param hExtraParams extra parameters
    2589             :  *
    2590             :  * @return CE_None on success or CE_Failure if something goes wrong.
    2591             :  *
    2592             :  */
    2593             : 
    2594      353016 : CPLErr GDALGridLinear(const void *poOptionsIn, GUInt32 nPoints,
    2595             :                       const double *padfX, const double *padfY,
    2596             :                       const double *padfZ, double dfXPoint, double dfYPoint,
    2597             :                       double *pdfValue, void *hExtraParams)
    2598             : {
    2599      353016 :     GDALGridExtraParameters *psExtraParams =
    2600             :         static_cast<GDALGridExtraParameters *>(hExtraParams);
    2601      353016 :     GDALTriangulation *psTriangulation = psExtraParams->psTriangulation;
    2602             : 
    2603      353016 :     int nOutputFacetIdx = -1;
    2604      353016 :     const bool bRet = CPL_TO_BOOL(GDALTriangulationFindFacetDirected(
    2605             :         psTriangulation, psExtraParams->nInitialFacetIdx, dfXPoint, dfYPoint,
    2606             :         &nOutputFacetIdx));
    2607             : 
    2608      353016 :     if (bRet)
    2609             :     {
    2610       86233 :         CPLAssert(nOutputFacetIdx >= 0);
    2611             :         // Reuse output facet idx as next initial index since we proceed line by
    2612             :         // line.
    2613       86233 :         psExtraParams->nInitialFacetIdx = nOutputFacetIdx;
    2614             : 
    2615       86233 :         double lambda1 = 0.0;
    2616       86233 :         double lambda2 = 0.0;
    2617       86233 :         double lambda3 = 0.0;
    2618       86233 :         GDALTriangulationComputeBarycentricCoordinates(
    2619             :             psTriangulation, nOutputFacetIdx, dfXPoint, dfYPoint, &lambda1,
    2620             :             &lambda2, &lambda3);
    2621       86233 :         const int i1 =
    2622       86233 :             psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[0];
    2623       86233 :         const int i2 =
    2624       86233 :             psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[1];
    2625       86233 :         const int i3 =
    2626       86233 :             psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[2];
    2627       86233 :         *pdfValue =
    2628       86233 :             lambda1 * padfZ[i1] + lambda2 * padfZ[i2] + lambda3 * padfZ[i3];
    2629             :     }
    2630             :     else
    2631             :     {
    2632      266783 :         if (nOutputFacetIdx >= 0)
    2633             :         {
    2634             :             // Also reuse this failed output facet, when valid, as seed for
    2635             :             // next search.
    2636      256088 :             psExtraParams->nInitialFacetIdx = nOutputFacetIdx;
    2637             :         }
    2638             : 
    2639      266783 :         const GDALGridLinearOptions *const poOptions =
    2640             :             static_cast<const GDALGridLinearOptions *>(poOptionsIn);
    2641      266783 :         const double dfRadius = poOptions->dfRadius;
    2642      266783 :         if (dfRadius == 0.0)
    2643             :         {
    2644      127804 :             *pdfValue = poOptions->dfNoDataValue;
    2645             :         }
    2646             :         else
    2647             :         {
    2648             :             GDALGridNearestNeighborOptions sNeighbourOptions;
    2649      138979 :             sNeighbourOptions.nSizeOfStructure = sizeof(sNeighbourOptions);
    2650      138979 :             sNeighbourOptions.dfRadius1 =
    2651      127804 :                 dfRadius < 0.0 || dfRadius >= std::numeric_limits<double>::max()
    2652      266783 :                     ? 0.0
    2653             :                     : dfRadius;
    2654      138979 :             sNeighbourOptions.dfRadius2 =
    2655      127804 :                 dfRadius < 0.0 || dfRadius >= std::numeric_limits<double>::max()
    2656      266783 :                     ? 0.0
    2657             :                     : dfRadius;
    2658      138979 :             sNeighbourOptions.dfAngle = 0.0;
    2659      138979 :             sNeighbourOptions.dfNoDataValue = poOptions->dfNoDataValue;
    2660      138979 :             return GDALGridNearestNeighbor(&sNeighbourOptions, nPoints, padfX,
    2661             :                                            padfY, padfZ, dfXPoint, dfYPoint,
    2662      138979 :                                            pdfValue, hExtraParams);
    2663             :         }
    2664             :     }
    2665             : 
    2666      214037 :     return CE_None;
    2667             : }
    2668             : 
    2669             : /************************************************************************/
    2670             : /*                             GDALGridJob                              */
    2671             : /************************************************************************/
    2672             : 
    2673             : typedef struct _GDALGridJob GDALGridJob;
    2674             : 
    2675             : struct _GDALGridJob
    2676             : {
    2677             :     GUInt32 nYStart;
    2678             : 
    2679             :     GByte *pabyData;
    2680             :     GUInt32 nYStep;
    2681             :     GUInt32 nXSize;
    2682             :     GUInt32 nYSize;
    2683             :     double dfXMin;
    2684             :     double dfYMin;
    2685             :     double dfDeltaX;
    2686             :     double dfDeltaY;
    2687             :     GUInt32 nPoints;
    2688             :     const double *padfX;
    2689             :     const double *padfY;
    2690             :     const double *padfZ;
    2691             :     const void *poOptions;
    2692             :     GDALGridFunction pfnGDALGridMethod;
    2693             :     GDALGridExtraParameters *psExtraParameters;
    2694             :     int (*pfnProgress)(GDALGridJob *psJob);
    2695             :     GDALDataType eType;
    2696             : 
    2697             :     int *pnCounter;
    2698             :     int nCounterSingleThreaded;
    2699             :     volatile int *pbStop;
    2700             :     CPLCond *hCond;
    2701             :     CPLMutex *hCondMutex;
    2702             : 
    2703             :     GDALProgressFunc pfnRealProgress;
    2704             :     void *pRealProgressArg;
    2705             : };
    2706             : 
    2707             : /************************************************************************/
    2708             : /*                    GDALGridProgressMultiThread()                     */
    2709             : /************************************************************************/
    2710             : 
    2711             : // Return TRUE if the computation must be interrupted.
    2712       21388 : static int GDALGridProgressMultiThread(GDALGridJob *psJob)
    2713             : {
    2714       21388 :     CPLAcquireMutex(psJob->hCondMutex, 1.0);
    2715       21388 :     ++(*psJob->pnCounter);
    2716       21388 :     CPLCondSignal(psJob->hCond);
    2717       21388 :     const int bStop = *psJob->pbStop;
    2718       21388 :     CPLReleaseMutex(psJob->hCondMutex);
    2719             : 
    2720       21388 :     return bStop;
    2721             : }
    2722             : 
    2723             : /************************************************************************/
    2724             : /*                     GDALGridProgressMonoThread()                     */
    2725             : /************************************************************************/
    2726             : 
    2727             : // Return TRUE if the computation must be interrupted.
    2728          40 : static int GDALGridProgressMonoThread(GDALGridJob *psJob)
    2729             : {
    2730          40 :     const int nCounter = ++(psJob->nCounterSingleThreaded);
    2731          40 :     if (!psJob->pfnRealProgress(nCounter / static_cast<double>(psJob->nYSize),
    2732             :                                 "", psJob->pRealProgressArg))
    2733             :     {
    2734           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    2735           0 :         *psJob->pbStop = TRUE;
    2736           0 :         return TRUE;
    2737             :     }
    2738          40 :     return FALSE;
    2739             : }
    2740             : 
    2741             : /************************************************************************/
    2742             : /*                         GDALGridJobProcess()                         */
    2743             : /************************************************************************/
    2744             : 
    2745         826 : static void GDALGridJobProcess(void *user_data)
    2746             : {
    2747         826 :     GDALGridJob *const psJob = static_cast<GDALGridJob *>(user_data);
    2748         826 :     int (*pfnProgress)(GDALGridJob *psJob) = psJob->pfnProgress;
    2749         826 :     const GUInt32 nXSize = psJob->nXSize;
    2750             : 
    2751             :     /* -------------------------------------------------------------------- */
    2752             :     /*  Allocate a buffer of scanline size, fill it with gridded values     */
    2753             :     /*  and use GDALCopyWords() to copy values into output data array with  */
    2754             :     /*  appropriate data type conversion.                                   */
    2755             :     /* -------------------------------------------------------------------- */
    2756             :     double *padfValues =
    2757         826 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(sizeof(double), nXSize));
    2758         826 :     if (padfValues == nullptr)
    2759             :     {
    2760           0 :         *(psJob->pbStop) = TRUE;
    2761           0 :         if (pfnProgress != nullptr)
    2762           0 :             pfnProgress(psJob);  // To notify the main thread.
    2763           0 :         return;
    2764             :     }
    2765             : 
    2766         826 :     const GUInt32 nYStart = psJob->nYStart;
    2767         826 :     const GUInt32 nYStep = psJob->nYStep;
    2768         826 :     GByte *pabyData = psJob->pabyData;
    2769             : 
    2770         826 :     const GUInt32 nYSize = psJob->nYSize;
    2771         826 :     const double dfXMin = psJob->dfXMin;
    2772         826 :     const double dfYMin = psJob->dfYMin;
    2773         826 :     const double dfDeltaX = psJob->dfDeltaX;
    2774         826 :     const double dfDeltaY = psJob->dfDeltaY;
    2775         826 :     const GUInt32 nPoints = psJob->nPoints;
    2776         826 :     const double *padfX = psJob->padfX;
    2777         826 :     const double *padfY = psJob->padfY;
    2778         826 :     const double *padfZ = psJob->padfZ;
    2779         826 :     const void *poOptions = psJob->poOptions;
    2780         826 :     GDALGridFunction pfnGDALGridMethod = psJob->pfnGDALGridMethod;
    2781             :     // Have a local copy of sExtraParameters since we want to modify
    2782             :     // nInitialFacetIdx.
    2783         826 :     GDALGridExtraParameters sExtraParameters = *psJob->psExtraParameters;
    2784         826 :     const GDALDataType eType = psJob->eType;
    2785             : 
    2786         826 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
    2787         826 :     const int nLineSpace = nXSize * nDataTypeSize;
    2788             : 
    2789       22254 :     for (GUInt32 nYPoint = nYStart; nYPoint < nYSize; nYPoint += nYStep)
    2790             :     {
    2791       21428 :         const double dfYPoint = dfYMin + (nYPoint + 0.5) * dfDeltaY;
    2792             : 
    2793     5323740 :         for (GUInt32 nXPoint = 0; nXPoint < nXSize; nXPoint++)
    2794             :         {
    2795     5302310 :             const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
    2796             : 
    2797    10604600 :             if ((*pfnGDALGridMethod)(poOptions, nPoints, padfX, padfY, padfZ,
    2798     5302310 :                                      dfXPoint, dfYPoint, padfValues + nXPoint,
    2799     5302310 :                                      &sExtraParameters) != CE_None)
    2800             :             {
    2801           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2802             :                          "Gridding failed at X position %lu, Y position %lu",
    2803             :                          static_cast<long unsigned int>(nXPoint),
    2804             :                          static_cast<long unsigned int>(nYPoint));
    2805           0 :                 *psJob->pbStop = TRUE;
    2806           0 :                 if (pfnProgress != nullptr)
    2807           0 :                     pfnProgress(psJob);  // To notify the main thread.
    2808           0 :                 break;
    2809             :             }
    2810             :         }
    2811             : 
    2812       21428 :         GDALCopyWords(padfValues, GDT_Float64, sizeof(double),
    2813       21428 :                       pabyData + nYPoint * nLineSpace, eType, nDataTypeSize,
    2814             :                       nXSize);
    2815             : 
    2816       21428 :         if (*psJob->pbStop || (pfnProgress != nullptr && pfnProgress(psJob)))
    2817           0 :             break;
    2818             :     }
    2819             : 
    2820         826 :     CPLFree(padfValues);
    2821             : }
    2822             : 
    2823             : /************************************************************************/
    2824             : /*                       GDALGridContextCreate()                        */
    2825             : /************************************************************************/
    2826             : 
    2827             : struct GDALGridContext
    2828             : {
    2829             :     GDALGridAlgorithm eAlgorithm;
    2830             :     void *poOptions;
    2831             :     GDALGridFunction pfnGDALGridMethod;
    2832             : 
    2833             :     GUInt32 nPoints;
    2834             :     GDALGridPoint *pasGridPoints;
    2835             :     GDALGridXYArrays sXYArrays;
    2836             : 
    2837             :     GDALGridExtraParameters sExtraParameters;
    2838             :     double *padfX;
    2839             :     double *padfY;
    2840             :     double *padfZ;
    2841             :     bool bFreePadfXYZArrays;
    2842             : 
    2843             :     CPLWorkerThreadPool *poWorkerThreadPool;
    2844             : };
    2845             : 
    2846             : static void GDALGridContextCreateQuadTree(GDALGridContext *psContext);
    2847             : 
    2848             : /**
    2849             :  * Creates a context to do regular gridding from the scattered data.
    2850             :  *
    2851             :  * This function takes the arrays of X and Y coordinates and corresponding Z
    2852             :  * values as input to prepare computation of regular grid (or call it a raster)
    2853             :  * from these scattered data.
    2854             :  *
    2855             :  * On Intel/AMD i386/x86_64 architectures, some
    2856             :  * gridding methods will be optimized with SSE instructions (provided GDAL
    2857             :  * has been compiled with such support, and it is available at runtime).
    2858             :  * Currently, only 'invdist' algorithm with default parameters has an optimized
    2859             :  * implementation.
    2860             :  * This can provide substantial speed-up, but sometimes at the expense of
    2861             :  * reduced floating point precision. This can be disabled by setting the
    2862             :  * GDAL_USE_SSE configuration option to NO.
    2863             :  * A further optimized version can use the AVX
    2864             :  * instruction set. This can be disabled by setting the GDAL_USE_AVX
    2865             :  * configuration option to NO.
    2866             :  *
    2867             :  * It is possible to set the GDAL_NUM_THREADS
    2868             :  * configuration option to parallelize the processing. The value to set is
    2869             :  * the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the
    2870             :  * computer (default value).
    2871             :  *
    2872             :  * @param eAlgorithm Gridding method.
    2873             :  * @param poOptions Options to control chosen gridding method.
    2874             :  * @param nPoints Number of elements in input arrays.
    2875             :  * @param padfX Input array of X coordinates.
    2876             :  * @param padfY Input array of Y coordinates.
    2877             :  * @param padfZ Input array of Z values.
    2878             :  * @param bCallerWillKeepPointArraysAlive Whether the provided padfX, padfY,
    2879             :  *        padfZ arrays will still be "alive" during the calls to
    2880             :  *        GDALGridContextProcess().  Setting to TRUE prevent them from being
    2881             :  *        duplicated in the context.  If unsure, set to FALSE.
    2882             :  *
    2883             :  * @return the context (to be freed with GDALGridContextFree()) or NULL in case
    2884             :  *         or error.
    2885             :  *
    2886             :  */
    2887             : 
    2888         208 : GDALGridContext *GDALGridContextCreate(GDALGridAlgorithm eAlgorithm,
    2889             :                                        const void *poOptions, GUInt32 nPoints,
    2890             :                                        const double *padfX, const double *padfY,
    2891             :                                        const double *padfZ,
    2892             :                                        int bCallerWillKeepPointArraysAlive)
    2893             : {
    2894         208 :     CPLAssert(poOptions);
    2895         208 :     CPLAssert(padfX);
    2896         208 :     CPLAssert(padfY);
    2897         208 :     CPLAssert(padfZ);
    2898         208 :     bool bCreateQuadTree = false;
    2899             : 
    2900             :     const unsigned int nPointCountThreshold =
    2901         208 :         atoi(CPLGetConfigOption("GDAL_GRID_POINT_COUNT_THRESHOLD", "100"));
    2902             : 
    2903             :     // Starting address aligned on 32-byte boundary for AVX.
    2904         208 :     float *pafXAligned = nullptr;
    2905         208 :     float *pafYAligned = nullptr;
    2906         208 :     float *pafZAligned = nullptr;
    2907             : 
    2908         208 :     void *poOptionsNew = nullptr;
    2909             : 
    2910         208 :     GDALGridFunction pfnGDALGridMethod = nullptr;
    2911             : 
    2912         208 :     switch (eAlgorithm)
    2913             :     {
    2914          45 :         case GGA_InverseDistanceToAPower:
    2915             :         {
    2916          45 :             const auto poOptionsOld =
    2917             :                 static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
    2918             :                     poOptions);
    2919          45 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    2920             :             {
    2921           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2922             :                          "Wrong value of nSizeOfStructure member");
    2923           0 :                 return nullptr;
    2924             :             }
    2925             :             poOptionsNew =
    2926          45 :                 CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
    2927          45 :             memcpy(poOptionsNew, poOptions,
    2928             :                    sizeof(GDALGridInverseDistanceToAPowerOptions));
    2929             : 
    2930          45 :             const GDALGridInverseDistanceToAPowerOptions *const poPower =
    2931             :                 static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
    2932             :                     poOptions);
    2933          45 :             if (poPower->dfRadius1 == 0.0 && poPower->dfRadius2 == 0.0)
    2934             :             {
    2935          36 :                 const double dfPower = poPower->dfPower;
    2936          36 :                 const double dfSmoothing = poPower->dfSmoothing;
    2937             : 
    2938          36 :                 pfnGDALGridMethod = GDALGridInverseDistanceToAPowerNoSearch;
    2939          36 :                 if (dfPower == 2.0 && dfSmoothing == 0.0)
    2940             :                 {
    2941             : #ifdef HAVE_AVX_AT_COMPILE_TIME
    2942             : 
    2943          34 :                     if (CPLTestBool(
    2944          62 :                             CPLGetConfigOption("GDAL_USE_AVX", "YES")) &&
    2945          28 :                         CPLHaveRuntimeAVX())
    2946             :                     {
    2947             :                         pafXAligned = static_cast<float *>(
    2948          28 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2949             :                                                             nPoints));
    2950             :                         pafYAligned = static_cast<float *>(
    2951          28 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2952             :                                                             nPoints));
    2953             :                         pafZAligned = static_cast<float *>(
    2954          28 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2955             :                                                             nPoints));
    2956          28 :                         if (pafXAligned != nullptr && pafYAligned != nullptr &&
    2957             :                             pafZAligned != nullptr)
    2958             :                         {
    2959          28 :                             CPLDebug("GDAL_GRID",
    2960             :                                      "Using AVX optimized version");
    2961          28 :                             pfnGDALGridMethod =
    2962             :                                 GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX;
    2963        1341 :                             for (GUInt32 i = 0; i < nPoints; i++)
    2964             :                             {
    2965        1313 :                                 pafXAligned[i] = static_cast<float>(padfX[i]);
    2966        1313 :                                 pafYAligned[i] = static_cast<float>(padfY[i]);
    2967        1313 :                                 pafZAligned[i] = static_cast<float>(padfZ[i]);
    2968          28 :                             }
    2969             :                         }
    2970             :                         else
    2971             :                         {
    2972           0 :                             VSIFree(pafXAligned);
    2973           0 :                             VSIFree(pafYAligned);
    2974           0 :                             VSIFree(pafZAligned);
    2975           0 :                             pafXAligned = nullptr;
    2976           0 :                             pafYAligned = nullptr;
    2977           0 :                             pafZAligned = nullptr;
    2978             :                         }
    2979             :                     }
    2980             : #endif
    2981             : 
    2982             : #ifdef HAVE_SSE_AT_COMPILE_TIME
    2983             : 
    2984          40 :                     if (pafXAligned == nullptr &&
    2985           6 :                         CPLTestBool(CPLGetConfigOption("GDAL_USE_SSE", "YES"))
    2986             : #if !defined(USE_NEON_OPTIMIZATIONS)
    2987          40 :                         && CPLHaveRuntimeSSE()
    2988             : #endif
    2989             :                     )
    2990             :                     {
    2991             :                         pafXAligned = static_cast<float *>(
    2992           3 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2993             :                                                             nPoints));
    2994             :                         pafYAligned = static_cast<float *>(
    2995           3 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2996             :                                                             nPoints));
    2997             :                         pafZAligned = static_cast<float *>(
    2998           3 :                             VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
    2999             :                                                             nPoints));
    3000           3 :                         if (pafXAligned != nullptr && pafYAligned != nullptr &&
    3001             :                             pafZAligned != nullptr)
    3002             :                         {
    3003           3 :                             CPLDebug("GDAL_GRID",
    3004             :                                      "Using SSE optimized version");
    3005           3 :                             pfnGDALGridMethod =
    3006             :                                 GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE;
    3007         405 :                             for (GUInt32 i = 0; i < nPoints; i++)
    3008             :                             {
    3009         402 :                                 pafXAligned[i] = static_cast<float>(padfX[i]);
    3010         402 :                                 pafYAligned[i] = static_cast<float>(padfY[i]);
    3011         402 :                                 pafZAligned[i] = static_cast<float>(padfZ[i]);
    3012           3 :                             }
    3013             :                         }
    3014             :                         else
    3015             :                         {
    3016           0 :                             VSIFree(pafXAligned);
    3017           0 :                             VSIFree(pafYAligned);
    3018           0 :                             VSIFree(pafZAligned);
    3019           0 :                             pafXAligned = nullptr;
    3020           0 :                             pafYAligned = nullptr;
    3021           0 :                             pafZAligned = nullptr;
    3022             :                         }
    3023             :                     }
    3024             : #endif  // HAVE_SSE_AT_COMPILE_TIME
    3025          36 :                 }
    3026             :             }
    3027             :             else
    3028             :             {
    3029           9 :                 pfnGDALGridMethod = GDALGridInverseDistanceToAPower;
    3030             :             }
    3031          45 :             break;
    3032             :         }
    3033          22 :         case GGA_InverseDistanceToAPowerNearestNeighbor:
    3034             :         {
    3035          22 :             const auto poOptionsOld = static_cast<
    3036             :                 const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
    3037             :                 poOptions);
    3038          22 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3039             :             {
    3040           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3041             :                          "Wrong value of nSizeOfStructure member");
    3042           0 :                 return nullptr;
    3043             :             }
    3044          22 :             poOptionsNew = CPLMalloc(
    3045             :                 sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
    3046          22 :             memcpy(
    3047             :                 poOptionsNew, poOptions,
    3048             :                 sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
    3049             : 
    3050          22 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3051          12 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3052             :             {
    3053          12 :                 pfnGDALGridMethod =
    3054             :                     GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant;
    3055             :             }
    3056             :             else
    3057             :             {
    3058          10 :                 pfnGDALGridMethod =
    3059             :                     GDALGridInverseDistanceToAPowerNearestNeighbor;
    3060             :             }
    3061          22 :             bCreateQuadTree = true;
    3062          22 :             break;
    3063             :         }
    3064          30 :         case GGA_MovingAverage:
    3065             :         {
    3066          30 :             const auto poOptionsOld =
    3067             :                 static_cast<const GDALGridMovingAverageOptions *>(poOptions);
    3068          30 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3069             :             {
    3070           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3071             :                          "Wrong value of nSizeOfStructure member");
    3072           0 :                 return nullptr;
    3073             :             }
    3074          30 :             poOptionsNew = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
    3075          30 :             memcpy(poOptionsNew, poOptions,
    3076             :                    sizeof(GDALGridMovingAverageOptions));
    3077             : 
    3078          30 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3079          22 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3080             :             {
    3081          11 :                 pfnGDALGridMethod = GDALGridMovingAveragePerQuadrant;
    3082          11 :                 bCreateQuadTree = true;
    3083             :             }
    3084             :             else
    3085             :             {
    3086          19 :                 pfnGDALGridMethod = GDALGridMovingAverage;
    3087          27 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3088           8 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3089           1 :                                     poOptionsOld->dfRadius2 > 0.0));
    3090             :             }
    3091          30 :             break;
    3092             :         }
    3093          21 :         case GGA_NearestNeighbor:
    3094             :         {
    3095          21 :             const auto poOptionsOld =
    3096             :                 static_cast<const GDALGridNearestNeighborOptions *>(poOptions);
    3097          21 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3098             :             {
    3099           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3100             :                          "Wrong value of nSizeOfStructure member");
    3101           0 :                 return nullptr;
    3102             :             }
    3103          21 :             poOptionsNew = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
    3104          21 :             memcpy(poOptionsNew, poOptions,
    3105             :                    sizeof(GDALGridNearestNeighborOptions));
    3106             : 
    3107          21 :             pfnGDALGridMethod = GDALGridNearestNeighbor;
    3108          34 :             bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3109          34 :                                poOptionsOld->dfAngle == 0.0 &&
    3110          13 :                                (poOptionsOld->dfRadius1 > 0.0 ||
    3111           5 :                                 poOptionsOld->dfRadius2 > 0.0));
    3112          21 :             break;
    3113             :         }
    3114          22 :         case GGA_MetricMinimum:
    3115             :         {
    3116          22 :             const auto poOptionsOld =
    3117             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3118          22 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3119             :             {
    3120           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3121             :                          "Wrong value of nSizeOfStructure member");
    3122           0 :                 return nullptr;
    3123             :             }
    3124          22 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3125          22 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3126             : 
    3127          22 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3128          16 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3129             :             {
    3130           9 :                 pfnGDALGridMethod = GDALGridDataMetricMinimumPerQuadrant;
    3131           9 :                 bCreateQuadTree = true;
    3132             :             }
    3133             :             else
    3134             :             {
    3135          13 :                 pfnGDALGridMethod = GDALGridDataMetricMinimum;
    3136          21 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3137           8 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3138           1 :                                     poOptionsOld->dfRadius2 > 0.0));
    3139             :             }
    3140          22 :             break;
    3141             :         }
    3142          16 :         case GGA_MetricMaximum:
    3143             :         {
    3144          16 :             const auto poOptionsOld =
    3145             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3146          16 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3147             :             {
    3148           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3149             :                          "Wrong value of nSizeOfStructure member");
    3150           0 :                 return nullptr;
    3151             :             }
    3152          16 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3153          16 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3154             : 
    3155          16 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3156          11 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3157             :             {
    3158           7 :                 pfnGDALGridMethod = GDALGridDataMetricMaximumPerQuadrant;
    3159           7 :                 bCreateQuadTree = true;
    3160             :             }
    3161             :             else
    3162             :             {
    3163           9 :                 pfnGDALGridMethod = GDALGridDataMetricMaximum;
    3164          17 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3165           8 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3166           1 :                                     poOptionsOld->dfRadius2 > 0.0));
    3167             :             }
    3168             : 
    3169          16 :             break;
    3170             :         }
    3171          13 :         case GGA_MetricRange:
    3172             :         {
    3173          13 :             const auto poOptionsOld =
    3174             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3175          13 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3176             :             {
    3177           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3178             :                          "Wrong value of nSizeOfStructure member");
    3179           0 :                 return nullptr;
    3180             :             }
    3181          13 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3182          13 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3183             : 
    3184          13 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3185           8 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3186             :             {
    3187           7 :                 pfnGDALGridMethod = GDALGridDataMetricRangePerQuadrant;
    3188           7 :                 bCreateQuadTree = true;
    3189             :             }
    3190             :             else
    3191             :             {
    3192           6 :                 pfnGDALGridMethod = GDALGridDataMetricRange;
    3193          11 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3194           5 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3195           1 :                                     poOptionsOld->dfRadius2 > 0.0));
    3196             :             }
    3197             : 
    3198          13 :             break;
    3199             :         }
    3200          15 :         case GGA_MetricCount:
    3201             :         {
    3202          15 :             const auto poOptionsOld =
    3203             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3204          15 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3205             :             {
    3206           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3207             :                          "Wrong value of nSizeOfStructure member");
    3208           0 :                 return nullptr;
    3209             :             }
    3210          15 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3211          15 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3212             : 
    3213          15 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3214          10 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3215             :             {
    3216           7 :                 pfnGDALGridMethod = GDALGridDataMetricCountPerQuadrant;
    3217           7 :                 bCreateQuadTree = true;
    3218             :             }
    3219             :             else
    3220             :             {
    3221           8 :                 pfnGDALGridMethod = GDALGridDataMetricCount;
    3222          15 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3223           7 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3224           0 :                                     poOptionsOld->dfRadius2 > 0.0));
    3225             :             }
    3226             : 
    3227          15 :             break;
    3228             :         }
    3229          13 :         case GGA_MetricAverageDistance:
    3230             :         {
    3231          13 :             const auto poOptionsOld =
    3232             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3233          13 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3234             :             {
    3235           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3236             :                          "Wrong value of nSizeOfStructure member");
    3237           0 :                 return nullptr;
    3238             :             }
    3239          13 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3240          13 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3241             : 
    3242          13 :             if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
    3243           8 :                 poOptionsOld->nMaxPointsPerQuadrant != 0)
    3244             :             {
    3245           7 :                 pfnGDALGridMethod =
    3246             :                     GDALGridDataMetricAverageDistancePerQuadrant;
    3247           7 :                 bCreateQuadTree = true;
    3248             :             }
    3249             :             else
    3250             :             {
    3251           6 :                 pfnGDALGridMethod = GDALGridDataMetricAverageDistance;
    3252          11 :                 bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3253           5 :                                    (poOptionsOld->dfRadius1 > 0.0 ||
    3254           1 :                                     poOptionsOld->dfRadius2 > 0.0));
    3255             :             }
    3256             : 
    3257          13 :             break;
    3258             :         }
    3259           4 :         case GGA_MetricAverageDistancePts:
    3260             :         {
    3261           4 :             const auto poOptionsOld =
    3262             :                 static_cast<const GDALGridDataMetricsOptions *>(poOptions);
    3263           4 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3264             :             {
    3265           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3266             :                          "Wrong value of nSizeOfStructure member");
    3267           0 :                 return nullptr;
    3268             :             }
    3269           4 :             poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    3270           4 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
    3271             : 
    3272           4 :             pfnGDALGridMethod = GDALGridDataMetricAverageDistancePts;
    3273           7 :             bCreateQuadTree = (nPoints > nPointCountThreshold &&
    3274           6 :                                poOptionsOld->dfAngle == 0.0 &&
    3275           2 :                                (poOptionsOld->dfRadius1 > 0.0 ||
    3276           0 :                                 poOptionsOld->dfRadius2 > 0.0));
    3277             : 
    3278           4 :             break;
    3279             :         }
    3280           7 :         case GGA_Linear:
    3281             :         {
    3282           7 :             const auto poOptionsOld =
    3283             :                 static_cast<const GDALGridLinearOptions *>(poOptions);
    3284           7 :             if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
    3285             :             {
    3286           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3287             :                          "Wrong value of nSizeOfStructure member");
    3288           0 :                 return nullptr;
    3289             :             }
    3290           7 :             poOptionsNew = CPLMalloc(sizeof(GDALGridLinearOptions));
    3291           7 :             memcpy(poOptionsNew, poOptions, sizeof(GDALGridLinearOptions));
    3292             : 
    3293           7 :             pfnGDALGridMethod = GDALGridLinear;
    3294           7 :             break;
    3295             :         }
    3296           0 :         default:
    3297           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    3298             :                      "GDAL does not support gridding method %d", eAlgorithm);
    3299           0 :             return nullptr;
    3300             :     }
    3301             : 
    3302         208 :     if (pafXAligned == nullptr && !bCallerWillKeepPointArraysAlive)
    3303             :     {
    3304             :         double *padfXNew =
    3305           0 :             static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
    3306             :         double *padfYNew =
    3307           0 :             static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
    3308             :         double *padfZNew =
    3309           0 :             static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
    3310           0 :         if (padfXNew == nullptr || padfYNew == nullptr || padfZNew == nullptr)
    3311             :         {
    3312           0 :             VSIFree(padfXNew);
    3313           0 :             VSIFree(padfYNew);
    3314           0 :             VSIFree(padfZNew);
    3315           0 :             CPLFree(poOptionsNew);
    3316           0 :             return nullptr;
    3317             :         }
    3318           0 :         memcpy(padfXNew, padfX, nPoints * sizeof(double));
    3319           0 :         memcpy(padfYNew, padfY, nPoints * sizeof(double));
    3320           0 :         memcpy(padfZNew, padfZ, nPoints * sizeof(double));
    3321           0 :         padfX = padfXNew;
    3322           0 :         padfY = padfYNew;
    3323           0 :         padfZ = padfZNew;
    3324             :     }
    3325             :     GDALGridContext *psContext =
    3326         208 :         static_cast<GDALGridContext *>(CPLCalloc(1, sizeof(GDALGridContext)));
    3327         208 :     psContext->eAlgorithm = eAlgorithm;
    3328         208 :     psContext->poOptions = poOptionsNew;
    3329         208 :     psContext->pfnGDALGridMethod = pfnGDALGridMethod;
    3330         208 :     psContext->nPoints = nPoints;
    3331         208 :     psContext->pasGridPoints = nullptr;
    3332         208 :     psContext->sXYArrays.padfX = padfX;
    3333         208 :     psContext->sXYArrays.padfY = padfY;
    3334         208 :     psContext->sExtraParameters.hQuadTree = nullptr;
    3335         208 :     psContext->sExtraParameters.dfInitialSearchRadius = 0.0;
    3336         208 :     psContext->sExtraParameters.pafX = pafXAligned;
    3337         208 :     psContext->sExtraParameters.pafY = pafYAligned;
    3338         208 :     psContext->sExtraParameters.pafZ = pafZAligned;
    3339         208 :     psContext->sExtraParameters.psTriangulation = nullptr;
    3340         208 :     psContext->sExtraParameters.nInitialFacetIdx = 0;
    3341         208 :     psContext->padfX = pafXAligned ? nullptr : const_cast<double *>(padfX);
    3342         208 :     psContext->padfY = pafXAligned ? nullptr : const_cast<double *>(padfY);
    3343         208 :     psContext->padfZ = pafXAligned ? nullptr : const_cast<double *>(padfZ);
    3344         208 :     psContext->bFreePadfXYZArrays =
    3345         208 :         pafXAligned ? false : !bCallerWillKeepPointArraysAlive;
    3346             : 
    3347             :     /* -------------------------------------------------------------------- */
    3348             :     /*  Create quadtree if requested and possible.                          */
    3349             :     /* -------------------------------------------------------------------- */
    3350         208 :     if (bCreateQuadTree)
    3351             :     {
    3352         116 :         GDALGridContextCreateQuadTree(psContext);
    3353         116 :         if (psContext->sExtraParameters.hQuadTree == nullptr &&
    3354           0 :             (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor ||
    3355             :              pfnGDALGridMethod == GDALGridMovingAveragePerQuadrant))
    3356             :         {
    3357             :             // shouldn't happen unless memory allocation failure occurs
    3358           0 :             GDALGridContextFree(psContext);
    3359           0 :             return nullptr;
    3360             :         }
    3361             :     }
    3362             : 
    3363             :     /* -------------------------------------------------------------------- */
    3364             :     /*  Pre-compute extra parameters in GDALGridExtraParameters              */
    3365             :     /* -------------------------------------------------------------------- */
    3366         208 :     if (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor)
    3367             :     {
    3368          22 :         const double dfPower =
    3369             :             static_cast<
    3370             :                 const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
    3371             :                 poOptions)
    3372             :                 ->dfPower;
    3373          22 :         psContext->sExtraParameters.dfPowerDiv2PreComp = dfPower / 2;
    3374             : 
    3375          22 :         const double dfRadius =
    3376             :             static_cast<
    3377             :                 const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
    3378             :                 poOptions)
    3379             :                 ->dfRadius;
    3380          22 :         psContext->sExtraParameters.dfRadiusPower2PreComp = pow(dfRadius, 2);
    3381             :     }
    3382             : 
    3383         208 :     if (eAlgorithm == GGA_Linear)
    3384             :     {
    3385           7 :         psContext->sExtraParameters.psTriangulation =
    3386           7 :             GDALTriangulationCreateDelaunay(nPoints, padfX, padfY);
    3387           7 :         if (psContext->sExtraParameters.psTriangulation == nullptr)
    3388             :         {
    3389           0 :             GDALGridContextFree(psContext);
    3390           0 :             return nullptr;
    3391             :         }
    3392           7 :         GDALTriangulationComputeBarycentricCoefficients(
    3393             :             psContext->sExtraParameters.psTriangulation, padfX, padfY);
    3394             :     }
    3395             : 
    3396             :     /* -------------------------------------------------------------------- */
    3397             :     /*  Start thread pool.                                                  */
    3398             :     /* -------------------------------------------------------------------- */
    3399         208 :     const int nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
    3400             :                                            /* bDefaultToAllCPUs = */ true);
    3401         208 :     if (nThreads > 1)
    3402             :     {
    3403         206 :         psContext->poWorkerThreadPool = new CPLWorkerThreadPool();
    3404         206 :         if (!psContext->poWorkerThreadPool->Setup(nThreads, nullptr, nullptr))
    3405             :         {
    3406           0 :             delete psContext->poWorkerThreadPool;
    3407           0 :             psContext->poWorkerThreadPool = nullptr;
    3408             :         }
    3409             :         else
    3410             :         {
    3411         206 :             CPLDebug("GDAL_GRID", "Using %d threads", nThreads);
    3412             :         }
    3413             :     }
    3414             :     else
    3415           2 :         psContext->poWorkerThreadPool = nullptr;
    3416             : 
    3417         208 :     return psContext;
    3418             : }
    3419             : 
    3420             : /************************************************************************/
    3421             : /*                   GDALGridContextCreateQuadTree()                    */
    3422             : /************************************************************************/
    3423             : 
    3424         122 : void GDALGridContextCreateQuadTree(GDALGridContext *psContext)
    3425             : {
    3426         122 :     const GUInt32 nPoints = psContext->nPoints;
    3427         122 :     psContext->pasGridPoints = static_cast<GDALGridPoint *>(
    3428         122 :         VSI_MALLOC2_VERBOSE(nPoints, sizeof(GDALGridPoint)));
    3429         122 :     if (psContext->pasGridPoints != nullptr)
    3430             :     {
    3431         122 :         const double *const padfX = psContext->padfX;
    3432         122 :         const double *const padfY = psContext->padfY;
    3433             : 
    3434             :         // Determine point extents.
    3435             :         CPLRectObj sRect;
    3436         122 :         sRect.minx = padfX[0];
    3437         122 :         sRect.miny = padfY[0];
    3438         122 :         sRect.maxx = padfX[0];
    3439         122 :         sRect.maxy = padfY[0];
    3440       29815 :         for (GUInt32 i = 1; i < nPoints; i++)
    3441             :         {
    3442       29693 :             if (padfX[i] < sRect.minx)
    3443          64 :                 sRect.minx = padfX[i];
    3444       29693 :             if (padfY[i] < sRect.miny)
    3445         864 :                 sRect.miny = padfY[i];
    3446       29693 :             if (padfX[i] > sRect.maxx)
    3447         870 :                 sRect.maxx = padfX[i];
    3448       29693 :             if (padfY[i] > sRect.maxy)
    3449          66 :                 sRect.maxy = padfY[i];
    3450             :         }
    3451             : 
    3452             :         // Initial value for search radius is the typical dimension of a
    3453             :         // "pixel" of the point array (assuming rather uniform distribution).
    3454         122 :         psContext->sExtraParameters.dfInitialSearchRadius = sqrt(
    3455         122 :             (sRect.maxx - sRect.minx) * (sRect.maxy - sRect.miny) / nPoints);
    3456             : 
    3457         122 :         psContext->sExtraParameters.hQuadTree =
    3458         122 :             CPLQuadTreeCreate(&sRect, GDALGridGetPointBounds);
    3459             : 
    3460       29937 :         for (GUInt32 i = 0; i < nPoints; i++)
    3461             :         {
    3462       29815 :             psContext->pasGridPoints[i].psXYArrays = &(psContext->sXYArrays);
    3463       29815 :             psContext->pasGridPoints[i].i = i;
    3464       29815 :             CPLQuadTreeInsert(psContext->sExtraParameters.hQuadTree,
    3465       29815 :                               psContext->pasGridPoints + i);
    3466             :         }
    3467             :     }
    3468         122 : }
    3469             : 
    3470             : /************************************************************************/
    3471             : /*                        GDALGridContextFree()                         */
    3472             : /************************************************************************/
    3473             : 
    3474             : /**
    3475             :  * Free a context used created by GDALGridContextCreate()
    3476             :  *
    3477             :  * @param psContext the context.
    3478             :  *
    3479             :  */
    3480         208 : void GDALGridContextFree(GDALGridContext *psContext)
    3481             : {
    3482         208 :     if (psContext)
    3483             :     {
    3484         208 :         CPLFree(psContext->poOptions);
    3485         208 :         CPLFree(psContext->pasGridPoints);
    3486         208 :         if (psContext->sExtraParameters.hQuadTree != nullptr)
    3487         122 :             CPLQuadTreeDestroy(psContext->sExtraParameters.hQuadTree);
    3488         208 :         if (psContext->bFreePadfXYZArrays)
    3489             :         {
    3490           0 :             CPLFree(psContext->padfX);
    3491           0 :             CPLFree(psContext->padfY);
    3492           0 :             CPLFree(psContext->padfZ);
    3493             :         }
    3494         208 :         VSIFreeAligned(psContext->sExtraParameters.pafX);
    3495         208 :         VSIFreeAligned(psContext->sExtraParameters.pafY);
    3496         208 :         VSIFreeAligned(psContext->sExtraParameters.pafZ);
    3497         208 :         if (psContext->sExtraParameters.psTriangulation)
    3498           7 :             GDALTriangulationFree(psContext->sExtraParameters.psTriangulation);
    3499         208 :         delete psContext->poWorkerThreadPool;
    3500         208 :         CPLFree(psContext);
    3501             :     }
    3502         208 : }
    3503             : 
    3504             : /************************************************************************/
    3505             : /*                       GDALGridContextProcess()                       */
    3506             : /************************************************************************/
    3507             : 
    3508             : /**
    3509             :  * Do the gridding of a window of a raster.
    3510             :  *
    3511             :  * This function takes the gridding context as input to prepare computation
    3512             :  * of regular grid (or call it a raster) from these scattered data.
    3513             :  * You should supply the extent of the output grid and allocate array
    3514             :  * sufficient to hold such a grid.
    3515             :  *
    3516             :  * @param psContext Gridding context.
    3517             :  * @param dfXMin Lowest X border of output grid.
    3518             :  * @param dfXMax Highest X border of output grid.
    3519             :  * @param dfYMin Lowest Y border of output grid.
    3520             :  * @param dfYMax Highest Y border of output grid.
    3521             :  * @param nXSize Number of columns in output grid.
    3522             :  * @param nYSize Number of rows in output grid.
    3523             :  * @param eType Data type of output array.
    3524             :  * @param pData Pointer to array where the computed grid will be stored.
    3525             :  * @param pfnProgress a GDALProgressFunc() compatible callback function for
    3526             :  * reporting progress or NULL.
    3527             :  * @param pProgressArg argument to be passed to pfnProgress.  May be NULL.
    3528             :  *
    3529             :  * @return CE_None on success or CE_Failure if something goes wrong.
    3530             :  *
    3531             :  */
    3532             : 
    3533         208 : CPLErr GDALGridContextProcess(GDALGridContext *psContext, double dfXMin,
    3534             :                               double dfXMax, double dfYMin, double dfYMax,
    3535             :                               GUInt32 nXSize, GUInt32 nYSize,
    3536             :                               GDALDataType eType, void *pData,
    3537             :                               GDALProgressFunc pfnProgress, void *pProgressArg)
    3538             : {
    3539         208 :     CPLAssert(psContext);
    3540         208 :     CPLAssert(pData);
    3541             : 
    3542         208 :     if (nXSize == 0 || nYSize == 0)
    3543             :     {
    3544           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    3545             :                  "Output raster dimensions should have non-zero size.");
    3546           0 :         return CE_Failure;
    3547             :     }
    3548             : 
    3549         208 :     const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
    3550         208 :     const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
    3551             : 
    3552             :     // For linear, check if we will need to fallback to nearest neighbour
    3553             :     // by sampling along the edges.  If all points on edges are within
    3554             :     // triangles, then interior points will also be.
    3555         208 :     if (psContext->eAlgorithm == GGA_Linear &&
    3556           7 :         psContext->sExtraParameters.hQuadTree == nullptr)
    3557             :     {
    3558           7 :         bool bNeedNearest = false;
    3559           7 :         int nStartLeft = 0;
    3560           7 :         int nStartRight = 0;
    3561           7 :         const double dfXPointMin = dfXMin + (0 + 0.5) * dfDeltaX;
    3562           7 :         const double dfXPointMax = dfXMin + (nXSize - 1 + 0.5) * dfDeltaX;
    3563         269 :         for (GUInt32 nYPoint = 0; !bNeedNearest && nYPoint < nYSize; nYPoint++)
    3564             :         {
    3565         262 :             const double dfYPoint = dfYMin + (nYPoint + 0.5) * dfDeltaY;
    3566             : 
    3567         262 :             if (!GDALTriangulationFindFacetDirected(
    3568         262 :                     psContext->sExtraParameters.psTriangulation, nStartLeft,
    3569             :                     dfXPointMin, dfYPoint, &nStartLeft))
    3570             :             {
    3571           6 :                 bNeedNearest = true;
    3572             :             }
    3573         262 :             if (!GDALTriangulationFindFacetDirected(
    3574         262 :                     psContext->sExtraParameters.psTriangulation, nStartRight,
    3575             :                     dfXPointMax, dfYPoint, &nStartRight))
    3576             :             {
    3577           6 :                 bNeedNearest = true;
    3578             :             }
    3579             :         }
    3580           7 :         int nStartTop = 0;
    3581           7 :         int nStartBottom = 0;
    3582           7 :         const double dfYPointMin = dfYMin + (0 + 0.5) * dfDeltaY;
    3583           7 :         const double dfYPointMax = dfYMin + (nYSize - 1 + 0.5) * dfDeltaY;
    3584         261 :         for (GUInt32 nXPoint = 1; !bNeedNearest && nXPoint + 1 < nXSize;
    3585             :              nXPoint++)
    3586             :         {
    3587         254 :             const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
    3588             : 
    3589         254 :             if (!GDALTriangulationFindFacetDirected(
    3590         254 :                     psContext->sExtraParameters.psTriangulation, nStartTop,
    3591             :                     dfXPoint, dfYPointMin, &nStartTop))
    3592             :             {
    3593           0 :                 bNeedNearest = true;
    3594             :             }
    3595         254 :             if (!GDALTriangulationFindFacetDirected(
    3596         254 :                     psContext->sExtraParameters.psTriangulation, nStartBottom,
    3597             :                     dfXPoint, dfYPointMax, &nStartBottom))
    3598             :             {
    3599           0 :                 bNeedNearest = true;
    3600             :             }
    3601             :         }
    3602           7 :         if (bNeedNearest)
    3603             :         {
    3604           6 :             CPLDebug("GDAL_GRID", "Will need nearest neighbour");
    3605           6 :             GDALGridContextCreateQuadTree(psContext);
    3606             :         }
    3607             :     }
    3608             : 
    3609         208 :     int nCounter = 0;
    3610         208 :     volatile int bStop = FALSE;
    3611             :     GDALGridJob sJob;
    3612         208 :     sJob.nYStart = 0;
    3613         208 :     sJob.pabyData = static_cast<GByte *>(pData);
    3614         208 :     sJob.nYStep = 1;
    3615         208 :     sJob.nXSize = nXSize;
    3616         208 :     sJob.nYSize = nYSize;
    3617         208 :     sJob.dfXMin = dfXMin;
    3618         208 :     sJob.dfYMin = dfYMin;
    3619         208 :     sJob.dfDeltaX = dfDeltaX;
    3620         208 :     sJob.dfDeltaY = dfDeltaY;
    3621         208 :     sJob.nPoints = psContext->nPoints;
    3622         208 :     sJob.padfX = psContext->padfX;
    3623         208 :     sJob.padfY = psContext->padfY;
    3624         208 :     sJob.padfZ = psContext->padfZ;
    3625         208 :     sJob.poOptions = psContext->poOptions;
    3626         208 :     sJob.pfnGDALGridMethod = psContext->pfnGDALGridMethod;
    3627         208 :     sJob.psExtraParameters = &psContext->sExtraParameters;
    3628         208 :     sJob.pfnProgress = nullptr;
    3629         208 :     sJob.eType = eType;
    3630         208 :     sJob.pfnRealProgress = pfnProgress;
    3631         208 :     sJob.pRealProgressArg = pProgressArg;
    3632         208 :     sJob.nCounterSingleThreaded = 0;
    3633         208 :     sJob.pnCounter = &nCounter;
    3634         208 :     sJob.pbStop = &bStop;
    3635         208 :     sJob.hCond = nullptr;
    3636         208 :     sJob.hCondMutex = nullptr;
    3637             : 
    3638         208 :     if (psContext->poWorkerThreadPool == nullptr)
    3639             :     {
    3640           2 :         if (sJob.pfnRealProgress != nullptr &&
    3641           2 :             sJob.pfnRealProgress != GDALDummyProgress)
    3642             :         {
    3643           2 :             sJob.pfnProgress = GDALGridProgressMonoThread;
    3644             :         }
    3645             : 
    3646           2 :         GDALGridJobProcess(&sJob);
    3647             :     }
    3648             :     else
    3649             :     {
    3650         206 :         int nThreads = psContext->poWorkerThreadPool->GetThreadCount();
    3651             :         GDALGridJob *pasJobs = static_cast<GDALGridJob *>(
    3652         206 :             CPLMalloc(sizeof(GDALGridJob) * nThreads));
    3653             : 
    3654         206 :         sJob.nYStep = nThreads;
    3655         206 :         sJob.hCondMutex = CPLCreateMutex(); /* and  implicitly take the mutex */
    3656         206 :         sJob.hCond = CPLCreateCond();
    3657         206 :         sJob.pfnProgress = GDALGridProgressMultiThread;
    3658             : 
    3659             :         /* --------------------------------------------------------------------
    3660             :          */
    3661             :         /*      Start threads. */
    3662             :         /* --------------------------------------------------------------------
    3663             :          */
    3664        1030 :         for (int i = 0; i < nThreads && !bStop; i++)
    3665             :         {
    3666         824 :             memcpy(&pasJobs[i], &sJob, sizeof(GDALGridJob));
    3667         824 :             pasJobs[i].nYStart = i;
    3668         824 :             psContext->poWorkerThreadPool->SubmitJob(GDALGridJobProcess,
    3669         824 :                                                      &pasJobs[i]);
    3670             :         }
    3671             : 
    3672             :         /* --------------------------------------------------------------------
    3673             :          */
    3674             :         /*      Report progress. */
    3675             :         /* --------------------------------------------------------------------
    3676             :          */
    3677       11868 :         while (*(sJob.pnCounter) < static_cast<int>(nYSize) && !bStop)
    3678             :         {
    3679       11662 :             CPLCondWait(sJob.hCond, sJob.hCondMutex);
    3680             : 
    3681       11662 :             int nLocalCounter = *(sJob.pnCounter);
    3682       11662 :             CPLReleaseMutex(sJob.hCondMutex);
    3683             : 
    3684       23318 :             if (pfnProgress != nullptr &&
    3685       11656 :                 !pfnProgress(nLocalCounter / static_cast<double>(nYSize), "",
    3686             :                              pProgressArg))
    3687             :             {
    3688           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    3689           0 :                 bStop = TRUE;
    3690             :             }
    3691             : 
    3692       11662 :             CPLAcquireMutex(sJob.hCondMutex, 1.0);
    3693             :         }
    3694             : 
    3695             :         // Release mutex before joining threads, otherwise they will dead-lock
    3696             :         // forever in GDALGridProgressMultiThread().
    3697         206 :         CPLReleaseMutex(sJob.hCondMutex);
    3698             : 
    3699             :         /* --------------------------------------------------------------------
    3700             :          */
    3701             :         /*      Wait for all threads to complete and finish. */
    3702             :         /* --------------------------------------------------------------------
    3703             :          */
    3704         206 :         psContext->poWorkerThreadPool->WaitCompletion();
    3705             : 
    3706         206 :         CPLFree(pasJobs);
    3707         206 :         CPLDestroyCond(sJob.hCond);
    3708         206 :         CPLDestroyMutex(sJob.hCondMutex);
    3709             :     }
    3710             : 
    3711         208 :     return bStop ? CE_Failure : CE_None;
    3712             : }
    3713             : 
    3714             : /************************************************************************/
    3715             : /*                           GDALGridCreate()                           */
    3716             : /************************************************************************/
    3717             : 
    3718             : /**
    3719             :  * Create regular grid from the scattered data.
    3720             :  *
    3721             :  * This function takes the arrays of X and Y coordinates and corresponding Z
    3722             :  * values as input and computes regular grid (or call it a raster) from these
    3723             :  * scattered data. You should supply geometry and extent of the output grid
    3724             :  * and allocate array sufficient to hold such a grid.
    3725             :  *
    3726             :  * It is possible to set the GDAL_NUM_THREADS
    3727             :  * configuration option to parallelize the processing. The value to set is
    3728             :  * the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the
    3729             :  * computer (default value).
    3730             :  *
    3731             :  * On Intel/AMD i386/x86_64 architectures, some
    3732             :  * gridding methods will be optimized with SSE instructions (provided GDAL
    3733             :  * has been compiled with such support, and it is available at runtime).
    3734             :  * Currently, only 'invdist' algorithm with default parameters has an optimized
    3735             :  * implementation.
    3736             :  * This can provide substantial speed-up, but sometimes at the expense of
    3737             :  * reduced floating point precision. This can be disabled by setting the
    3738             :  * GDAL_USE_SSE configuration option to NO.
    3739             :  * A further optimized version can use the AVX
    3740             :  * instruction set. This can be disabled by setting the GDAL_USE_AVX
    3741             :  * configuration option to NO.
    3742             :  *
    3743             :  * Note: it will be more efficient to use GDALGridContextCreate(),
    3744             :  * GDALGridContextProcess() and GDALGridContextFree() when doing repeated
    3745             :  * gridding operations with the same algorithm, parameters and points, and
    3746             :  * moving the window in the output grid.
    3747             :  *
    3748             :  * @param eAlgorithm Gridding method.
    3749             :  * @param poOptions Options to control chosen gridding method.
    3750             :  * @param nPoints Number of elements in input arrays.
    3751             :  * @param padfX Input array of X coordinates.
    3752             :  * @param padfY Input array of Y coordinates.
    3753             :  * @param padfZ Input array of Z values.
    3754             :  * @param dfXMin Lowest X border of output grid.
    3755             :  * @param dfXMax Highest X border of output grid.
    3756             :  * @param dfYMin Lowest Y border of output grid.
    3757             :  * @param dfYMax Highest Y border of output grid.
    3758             :  * @param nXSize Number of columns in output grid.
    3759             :  * @param nYSize Number of rows in output grid.
    3760             :  * @param eType Data type of output array.
    3761             :  * @param pData Pointer to array where the computed grid will be stored.
    3762             :  * @param pfnProgress a GDALProgressFunc() compatible callback function for
    3763             :  * reporting progress or NULL.
    3764             :  * @param pProgressArg argument to be passed to pfnProgress.  May be NULL.
    3765             :  *
    3766             :  * @return CE_None on success or CE_Failure if something goes wrong.
    3767             :  */
    3768             : 
    3769           5 : CPLErr GDALGridCreate(GDALGridAlgorithm eAlgorithm, const void *poOptions,
    3770             :                       GUInt32 nPoints, const double *padfX, const double *padfY,
    3771             :                       const double *padfZ, double dfXMin, double dfXMax,
    3772             :                       double dfYMin, double dfYMax, GUInt32 nXSize,
    3773             :                       GUInt32 nYSize, GDALDataType eType, void *pData,
    3774             :                       GDALProgressFunc pfnProgress, void *pProgressArg)
    3775             : {
    3776           5 :     GDALGridContext *psContext = GDALGridContextCreate(
    3777             :         eAlgorithm, poOptions, nPoints, padfX, padfY, padfZ, TRUE);
    3778           5 :     CPLErr eErr = CE_Failure;
    3779           5 :     if (psContext)
    3780             :     {
    3781           5 :         eErr = GDALGridContextProcess(psContext, dfXMin, dfXMax, dfYMin, dfYMax,
    3782             :                                       nXSize, nYSize, eType, pData, pfnProgress,
    3783             :                                       pProgressArg);
    3784             :     }
    3785             : 
    3786           5 :     GDALGridContextFree(psContext);
    3787           5 :     return eErr;
    3788             : }
    3789             : 
    3790             : /************************************************************************/
    3791             : /*                  GDALGridParseAlgorithmAndOptions()                  */
    3792             : /************************************************************************/
    3793             : 
    3794             : /** Translates mnemonic gridding algorithm names into GDALGridAlgorithm code,
    3795             :  * parse control parameters and assign defaults.
    3796             :  */
    3797         433 : CPLErr GDALGridParseAlgorithmAndOptions(const char *pszAlgorithm,
    3798             :                                         GDALGridAlgorithm *peAlgorithm,
    3799             :                                         void **ppOptions)
    3800             : {
    3801         433 :     CPLAssert(pszAlgorithm);
    3802         433 :     CPLAssert(peAlgorithm);
    3803         433 :     CPLAssert(ppOptions);
    3804             : 
    3805         433 :     *ppOptions = nullptr;
    3806             : 
    3807         433 :     char **papszParams = CSLTokenizeString2(pszAlgorithm, ":", FALSE);
    3808             : 
    3809         433 :     if (CSLCount(papszParams) < 1)
    3810             :     {
    3811           0 :         CSLDestroy(papszParams);
    3812           0 :         return CE_Failure;
    3813             :     }
    3814             : 
    3815         433 :     if (EQUAL(papszParams[0], szAlgNameInvDist))
    3816             :     {
    3817         547 :         if (CSLFetchNameValue(papszParams, "min_points_per_quadrant") ||
    3818         272 :             CSLFetchNameValue(papszParams, "max_points_per_quadrant"))
    3819             :         {
    3820             :             // Remap invdist to invdistnn if per quadrant is required
    3821           4 :             if (CSLFetchNameValue(papszParams, "radius") == nullptr)
    3822             :             {
    3823             :                 const double dfRadius1 =
    3824           0 :                     CPLAtofM(CSLFetchNameValueDef(papszParams, "radius1", "1"));
    3825             :                 const double dfRadius2 =
    3826           0 :                     CPLAtofM(CSLFetchNameValueDef(papszParams, "radius2", "1"));
    3827           0 :                 if (dfRadius1 != dfRadius2)
    3828             :                 {
    3829           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    3830             :                              "radius1 != radius2 not supported when "
    3831             :                              "min_points_per_quadrant and/or "
    3832             :                              "max_points_per_quadrant is specified");
    3833           0 :                     CSLDestroy(papszParams);
    3834           0 :                     return CE_Failure;
    3835             :                 }
    3836             :             }
    3837             : 
    3838           4 :             if (CPLAtofM(CSLFetchNameValueDef(papszParams, "angle", "0")) != 0)
    3839             :             {
    3840           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    3841             :                          "angle != 0 not supported when "
    3842             :                          "min_points_per_quadrant and/or "
    3843             :                          "max_points_per_quadrant is specified");
    3844           0 :                 CSLDestroy(papszParams);
    3845           0 :                 return CE_Failure;
    3846             :             }
    3847             : 
    3848           4 :             char **papszNewParams = CSLAddString(nullptr, "invdistnn");
    3849           4 :             if (CSLFetchNameValue(papszParams, "radius") == nullptr)
    3850             :             {
    3851           0 :                 papszNewParams = CSLSetNameValue(
    3852             :                     papszNewParams, "radius",
    3853             :                     CSLFetchNameValueDef(papszParams, "radius1", "1"));
    3854             :             }
    3855          32 :             for (const char *pszItem :
    3856             :                  {"radius", "power", "smoothing", "max_points", "min_points",
    3857             :                   "nodata", "min_points_per_quadrant",
    3858          36 :                   "max_points_per_quadrant"})
    3859             :             {
    3860          32 :                 const char *pszValue = CSLFetchNameValue(papszParams, pszItem);
    3861          32 :                 if (pszValue)
    3862             :                     papszNewParams =
    3863          19 :                         CSLSetNameValue(papszNewParams, pszItem, pszValue);
    3864             :             }
    3865           4 :             CSLDestroy(papszParams);
    3866           4 :             papszParams = papszNewParams;
    3867             : 
    3868           4 :             *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
    3869             :         }
    3870             :         else
    3871             :         {
    3872         271 :             *peAlgorithm = GGA_InverseDistanceToAPower;
    3873             :         }
    3874             :     }
    3875         158 :     else if (EQUAL(papszParams[0], szAlgNameInvDistNearestNeighbor))
    3876             :     {
    3877          18 :         *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
    3878             :     }
    3879         140 :     else if (EQUAL(papszParams[0], szAlgNameAverage))
    3880             :     {
    3881          30 :         *peAlgorithm = GGA_MovingAverage;
    3882             :     }
    3883         110 :     else if (EQUAL(papszParams[0], szAlgNameNearest))
    3884             :     {
    3885          19 :         *peAlgorithm = GGA_NearestNeighbor;
    3886             :     }
    3887          91 :     else if (EQUAL(papszParams[0], szAlgNameMinimum))
    3888             :     {
    3889          22 :         *peAlgorithm = GGA_MetricMinimum;
    3890             :     }
    3891          69 :     else if (EQUAL(papszParams[0], szAlgNameMaximum))
    3892             :     {
    3893          16 :         *peAlgorithm = GGA_MetricMaximum;
    3894             :     }
    3895          53 :     else if (EQUAL(papszParams[0], szAlgNameRange))
    3896             :     {
    3897          13 :         *peAlgorithm = GGA_MetricRange;
    3898             :     }
    3899          40 :     else if (EQUAL(papszParams[0], szAlgNameCount))
    3900             :     {
    3901          15 :         *peAlgorithm = GGA_MetricCount;
    3902             :     }
    3903          25 :     else if (EQUAL(papszParams[0], szAlgNameAverageDistance))
    3904             :     {
    3905          13 :         *peAlgorithm = GGA_MetricAverageDistance;
    3906             :     }
    3907          12 :     else if (EQUAL(papszParams[0], szAlgNameAverageDistancePts))
    3908             :     {
    3909           4 :         *peAlgorithm = GGA_MetricAverageDistancePts;
    3910             :     }
    3911           8 :     else if (EQUAL(papszParams[0], szAlgNameLinear))
    3912             :     {
    3913           7 :         *peAlgorithm = GGA_Linear;
    3914             :     }
    3915             :     else
    3916             :     {
    3917           1 :         CPLError(CE_Failure, CPLE_IllegalArg,
    3918             :                  "Unsupported gridding method \"%s\"", papszParams[0]);
    3919           1 :         CSLDestroy(papszParams);
    3920           1 :         return CE_Failure;
    3921             :     }
    3922             : 
    3923             :     /* -------------------------------------------------------------------- */
    3924             :     /*      Parse algorithm parameters and assign defaults.                 */
    3925             :     /* -------------------------------------------------------------------- */
    3926         432 :     const char *const *papszKnownOptions = nullptr;
    3927             : 
    3928         432 :     switch (*peAlgorithm)
    3929             :     {
    3930         271 :         case GGA_InverseDistanceToAPower:
    3931             :         default:
    3932             :         {
    3933         271 :             *ppOptions =
    3934         271 :                 CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
    3935             : 
    3936         271 :             GDALGridInverseDistanceToAPowerOptions *const poPowerOpts =
    3937             :                 static_cast<GDALGridInverseDistanceToAPowerOptions *>(
    3938             :                     *ppOptions);
    3939             : 
    3940         271 :             poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
    3941             : 
    3942         271 :             const char *pszValue = CSLFetchNameValue(papszParams, "power");
    3943         271 :             poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
    3944             : 
    3945         271 :             pszValue = CSLFetchNameValue(papszParams, "smoothing");
    3946         271 :             poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
    3947             : 
    3948         271 :             pszValue = CSLFetchNameValue(papszParams, "radius");
    3949         271 :             if (pszValue)
    3950             :             {
    3951           5 :                 poPowerOpts->dfRadius1 = CPLAtofM(pszValue);
    3952           5 :                 poPowerOpts->dfRadius2 = poPowerOpts->dfRadius1;
    3953             :             }
    3954             :             else
    3955             :             {
    3956         266 :                 pszValue = CSLFetchNameValue(papszParams, "radius1");
    3957         266 :                 poPowerOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
    3958             : 
    3959         266 :                 pszValue = CSLFetchNameValue(papszParams, "radius2");
    3960         266 :                 poPowerOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
    3961             :             }
    3962             : 
    3963         271 :             pszValue = CSLFetchNameValue(papszParams, "angle");
    3964         271 :             poPowerOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
    3965             : 
    3966         271 :             pszValue = CSLFetchNameValue(papszParams, "max_points");
    3967         271 :             poPowerOpts->nMaxPoints =
    3968         271 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    3969             : 
    3970         271 :             pszValue = CSLFetchNameValue(papszParams, "min_points");
    3971         271 :             poPowerOpts->nMinPoints =
    3972         271 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    3973             : 
    3974         271 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    3975         271 :             poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
    3976             : 
    3977             :             static const char *const apszKnownOptions[] = {
    3978             :                 "power", "smoothing",  "radius",     "radius1", "radius2",
    3979             :                 "angle", "max_points", "min_points", "nodata",  nullptr};
    3980         271 :             papszKnownOptions = apszKnownOptions;
    3981             : 
    3982         271 :             break;
    3983             :         }
    3984          22 :         case GGA_InverseDistanceToAPowerNearestNeighbor:
    3985             :         {
    3986          22 :             *ppOptions = CPLMalloc(
    3987             :                 sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
    3988             : 
    3989             :             GDALGridInverseDistanceToAPowerNearestNeighborOptions *const
    3990          22 :                 poPowerOpts = static_cast<
    3991             :                     GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
    3992             :                     *ppOptions);
    3993             : 
    3994          22 :             poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
    3995             : 
    3996          22 :             const char *pszValue = CSLFetchNameValue(papszParams, "power");
    3997          22 :             poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
    3998             : 
    3999          22 :             pszValue = CSLFetchNameValue(papszParams, "smoothing");
    4000          22 :             poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
    4001             : 
    4002          22 :             pszValue = CSLFetchNameValue(papszParams, "radius");
    4003          22 :             poPowerOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : 1.0;
    4004          22 :             if (!(poPowerOpts->dfRadius > 0))
    4005             :             {
    4006           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    4007             :                          "Radius value should be strictly positive");
    4008           0 :                 CSLDestroy(papszParams);
    4009           0 :                 return CE_Failure;
    4010             :             }
    4011             : 
    4012          22 :             pszValue = CSLFetchNameValue(papszParams, "max_points");
    4013          22 :             poPowerOpts->nMaxPoints =
    4014          22 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 12);
    4015             : 
    4016          22 :             pszValue = CSLFetchNameValue(papszParams, "min_points");
    4017          22 :             poPowerOpts->nMinPoints =
    4018          22 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4019             : 
    4020          22 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    4021          22 :             poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
    4022             : 
    4023             :             pszValue =
    4024          22 :                 CSLFetchNameValue(papszParams, "min_points_per_quadrant");
    4025          22 :             poPowerOpts->nMinPointsPerQuadrant =
    4026          22 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4027             : 
    4028             :             pszValue =
    4029          22 :                 CSLFetchNameValue(papszParams, "max_points_per_quadrant");
    4030          22 :             poPowerOpts->nMaxPointsPerQuadrant =
    4031          22 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4032             : 
    4033             :             static const char *const apszKnownOptions[] = {
    4034             :                 "power",
    4035             :                 "smoothing",
    4036             :                 "radius",
    4037             :                 "max_points",
    4038             :                 "min_points",
    4039             :                 "nodata",
    4040             :                 "min_points_per_quadrant",
    4041             :                 "max_points_per_quadrant",
    4042             :                 nullptr};
    4043          22 :             papszKnownOptions = apszKnownOptions;
    4044             : 
    4045          22 :             break;
    4046             :         }
    4047          30 :         case GGA_MovingAverage:
    4048             :         {
    4049          30 :             *ppOptions = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
    4050             : 
    4051          30 :             GDALGridMovingAverageOptions *const poAverageOpts =
    4052             :                 static_cast<GDALGridMovingAverageOptions *>(*ppOptions);
    4053             : 
    4054          30 :             poAverageOpts->nSizeOfStructure = sizeof(*poAverageOpts);
    4055             : 
    4056          30 :             const char *pszValue = CSLFetchNameValue(papszParams, "radius");
    4057          30 :             if (pszValue)
    4058             :             {
    4059          14 :                 poAverageOpts->dfRadius1 = CPLAtofM(pszValue);
    4060          14 :                 poAverageOpts->dfRadius2 = poAverageOpts->dfRadius1;
    4061             :             }
    4062             :             else
    4063             :             {
    4064          16 :                 pszValue = CSLFetchNameValue(papszParams, "radius1");
    4065          16 :                 poAverageOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
    4066             : 
    4067          16 :                 pszValue = CSLFetchNameValue(papszParams, "radius2");
    4068          16 :                 poAverageOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
    4069             :             }
    4070             : 
    4071          30 :             pszValue = CSLFetchNameValue(papszParams, "angle");
    4072          30 :             poAverageOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
    4073             : 
    4074          30 :             pszValue = CSLFetchNameValue(papszParams, "min_points");
    4075          30 :             poAverageOpts->nMinPoints =
    4076          30 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4077             : 
    4078          30 :             pszValue = CSLFetchNameValue(papszParams, "max_points");
    4079          30 :             poAverageOpts->nMaxPoints =
    4080          30 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4081             : 
    4082          30 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    4083          30 :             poAverageOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
    4084             : 
    4085             :             pszValue =
    4086          30 :                 CSLFetchNameValue(papszParams, "min_points_per_quadrant");
    4087          30 :             poAverageOpts->nMinPointsPerQuadrant =
    4088          30 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4089             : 
    4090             :             pszValue =
    4091          30 :                 CSLFetchNameValue(papszParams, "max_points_per_quadrant");
    4092          30 :             poAverageOpts->nMaxPointsPerQuadrant =
    4093          30 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4094             : 
    4095          30 :             if (poAverageOpts->nMinPointsPerQuadrant != 0 ||
    4096          22 :                 poAverageOpts->nMaxPointsPerQuadrant != 0)
    4097             :             {
    4098          11 :                 if (!(poAverageOpts->dfRadius1 > 0) ||
    4099          11 :                     !(poAverageOpts->dfRadius2 > 0))
    4100             :                 {
    4101           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    4102             :                              "Radius value should be strictly positive when "
    4103             :                              "per quadrant parameters are specified");
    4104           0 :                     CSLDestroy(papszParams);
    4105           0 :                     return CE_Failure;
    4106             :                 }
    4107             :             }
    4108          19 :             else if (poAverageOpts->nMaxPoints > 0)
    4109             :             {
    4110           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4111             :                          "max_points is ignored unless one of "
    4112             :                          "min_points_per_quadrant or max_points_per_quadrant "
    4113             :                          "is >= 1");
    4114             :             }
    4115             : 
    4116             :             static const char *const apszKnownOptions[] = {
    4117             :                 "radius",
    4118             :                 "radius1",
    4119             :                 "radius2",
    4120             :                 "angle",
    4121             :                 "min_points",
    4122             :                 "max_points",
    4123             :                 "nodata",
    4124             :                 "min_points_per_quadrant",
    4125             :                 "max_points_per_quadrant",
    4126             :                 nullptr};
    4127          30 :             papszKnownOptions = apszKnownOptions;
    4128             : 
    4129          30 :             break;
    4130             :         }
    4131          19 :         case GGA_NearestNeighbor:
    4132             :         {
    4133          19 :             *ppOptions = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
    4134             : 
    4135          19 :             GDALGridNearestNeighborOptions *const poNeighborOpts =
    4136             :                 static_cast<GDALGridNearestNeighborOptions *>(*ppOptions);
    4137             : 
    4138          19 :             poNeighborOpts->nSizeOfStructure = sizeof(*poNeighborOpts);
    4139             : 
    4140          19 :             const char *pszValue = CSLFetchNameValue(papszParams, "radius");
    4141          19 :             if (pszValue)
    4142             :             {
    4143           2 :                 poNeighborOpts->dfRadius1 = CPLAtofM(pszValue);
    4144           2 :                 poNeighborOpts->dfRadius2 = poNeighborOpts->dfRadius1;
    4145             :             }
    4146             :             else
    4147             :             {
    4148          17 :                 pszValue = CSLFetchNameValue(papszParams, "radius1");
    4149          17 :                 poNeighborOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
    4150             : 
    4151          17 :                 pszValue = CSLFetchNameValue(papszParams, "radius2");
    4152          17 :                 poNeighborOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
    4153             :             }
    4154             : 
    4155          19 :             pszValue = CSLFetchNameValue(papszParams, "angle");
    4156          19 :             poNeighborOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
    4157             : 
    4158          19 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    4159          19 :             poNeighborOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
    4160             : 
    4161             :             static const char *const apszKnownOptions[] = {
    4162             :                 "radius", "radius1", "radius2", "angle", "nodata", nullptr};
    4163          19 :             papszKnownOptions = apszKnownOptions;
    4164             : 
    4165          19 :             break;
    4166             :         }
    4167          83 :         case GGA_MetricMinimum:
    4168             :         case GGA_MetricMaximum:
    4169             :         case GGA_MetricRange:
    4170             :         case GGA_MetricCount:
    4171             :         case GGA_MetricAverageDistance:
    4172             :         case GGA_MetricAverageDistancePts:
    4173             :         {
    4174          83 :             *ppOptions = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
    4175             : 
    4176          83 :             GDALGridDataMetricsOptions *const poMetricsOptions =
    4177             :                 static_cast<GDALGridDataMetricsOptions *>(*ppOptions);
    4178             : 
    4179          83 :             poMetricsOptions->nSizeOfStructure = sizeof(*poMetricsOptions);
    4180             : 
    4181          83 :             const char *pszValue = CSLFetchNameValue(papszParams, "radius");
    4182          83 :             if (pszValue)
    4183             :             {
    4184          31 :                 poMetricsOptions->dfRadius1 = CPLAtofM(pszValue);
    4185          31 :                 poMetricsOptions->dfRadius2 = poMetricsOptions->dfRadius1;
    4186             :             }
    4187             :             else
    4188             :             {
    4189          52 :                 pszValue = CSLFetchNameValue(papszParams, "radius1");
    4190          52 :                 poMetricsOptions->dfRadius1 =
    4191          52 :                     pszValue ? CPLAtofM(pszValue) : 0.0;
    4192             : 
    4193          52 :                 pszValue = CSLFetchNameValue(papszParams, "radius2");
    4194          52 :                 poMetricsOptions->dfRadius2 =
    4195          52 :                     pszValue ? CPLAtofM(pszValue) : 0.0;
    4196             :             }
    4197             : 
    4198          83 :             pszValue = CSLFetchNameValue(papszParams, "angle");
    4199          83 :             poMetricsOptions->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
    4200             : 
    4201          83 :             pszValue = CSLFetchNameValue(papszParams, "min_points");
    4202          83 :             poMetricsOptions->nMinPoints = pszValue ? atoi(pszValue) : 0;
    4203             : 
    4204          83 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    4205          83 :             poMetricsOptions->dfNoDataValue =
    4206          83 :                 pszValue ? CPLAtofM(pszValue) : 0.0;
    4207             : 
    4208             :             pszValue =
    4209          83 :                 CSLFetchNameValue(papszParams, "min_points_per_quadrant");
    4210          83 :             poMetricsOptions->nMinPointsPerQuadrant =
    4211          83 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4212             : 
    4213             :             pszValue =
    4214          83 :                 CSLFetchNameValue(papszParams, "max_points_per_quadrant");
    4215          83 :             poMetricsOptions->nMaxPointsPerQuadrant =
    4216          83 :                 static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
    4217             : 
    4218          83 :             if (poMetricsOptions->nMinPointsPerQuadrant != 0 ||
    4219          57 :                 poMetricsOptions->nMaxPointsPerQuadrant != 0)
    4220             :             {
    4221          37 :                 if (*peAlgorithm == GGA_MetricAverageDistancePts)
    4222             :                 {
    4223           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    4224             :                              "Algorithm %s not supported when "
    4225             :                              "per quadrant parameters are specified",
    4226             :                              szAlgNameAverageDistancePts);
    4227           0 :                     CSLDestroy(papszParams);
    4228           0 :                     return CE_Failure;
    4229             :                 }
    4230          37 :                 if (!(poMetricsOptions->dfRadius1 > 0) ||
    4231          37 :                     !(poMetricsOptions->dfRadius2 > 0))
    4232             :                 {
    4233           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    4234             :                              "Radius value should be strictly positive when "
    4235             :                              "per quadrant parameters are specified");
    4236           0 :                     CSLDestroy(papszParams);
    4237           0 :                     return CE_Failure;
    4238             :                 }
    4239             :             }
    4240             : 
    4241             :             static const char *const apszKnownOptions[] = {
    4242             :                 "radius",
    4243             :                 "radius1",
    4244             :                 "radius2",
    4245             :                 "angle",
    4246             :                 "min_points",
    4247             :                 "nodata",
    4248             :                 "min_points_per_quadrant",
    4249             :                 "max_points_per_quadrant",
    4250             :                 nullptr};
    4251          83 :             papszKnownOptions = apszKnownOptions;
    4252             : 
    4253          83 :             break;
    4254             :         }
    4255           7 :         case GGA_Linear:
    4256             :         {
    4257           7 :             *ppOptions = CPLMalloc(sizeof(GDALGridLinearOptions));
    4258             : 
    4259           7 :             GDALGridLinearOptions *const poLinearOpts =
    4260             :                 static_cast<GDALGridLinearOptions *>(*ppOptions);
    4261             : 
    4262           7 :             poLinearOpts->nSizeOfStructure = sizeof(*poLinearOpts);
    4263             : 
    4264           7 :             const char *pszValue = CSLFetchNameValue(papszParams, "radius");
    4265           7 :             poLinearOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : -1.0;
    4266             : 
    4267           7 :             pszValue = CSLFetchNameValue(papszParams, "nodata");
    4268           7 :             poLinearOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
    4269             : 
    4270             :             static const char *const apszKnownOptions[] = {"radius", "nodata",
    4271             :                                                            nullptr};
    4272           7 :             papszKnownOptions = apszKnownOptions;
    4273             : 
    4274           7 :             break;
    4275             :         }
    4276             :     }
    4277             : 
    4278         432 :     if (papszKnownOptions)
    4279             :     {
    4280        1222 :         for (int i = 1; papszParams[i] != nullptr; ++i)
    4281             :         {
    4282         790 :             char *pszKey = nullptr;
    4283         790 :             CPL_IGNORE_RET_VAL(CPLParseNameValue(papszParams[i], &pszKey));
    4284         790 :             if (pszKey)
    4285             :             {
    4286         790 :                 bool bKnownKey = false;
    4287        3324 :                 for (const char *const *papszKnownKeyIter = papszKnownOptions;
    4288        3324 :                      *papszKnownKeyIter; ++papszKnownKeyIter)
    4289             :                 {
    4290        3324 :                     if (EQUAL(*papszKnownKeyIter, pszKey))
    4291             :                     {
    4292         790 :                         bKnownKey = true;
    4293         790 :                         break;
    4294             :                     }
    4295             :                 }
    4296         790 :                 if (!bKnownKey)
    4297             :                 {
    4298           0 :                     CPLError(CE_Warning, CPLE_AppDefined, "Option %s ignored",
    4299             :                              pszKey);
    4300             :                 }
    4301             :             }
    4302         790 :             CPLFree(pszKey);
    4303             :         }
    4304             :     }
    4305             : 
    4306         432 :     CSLDestroy(papszParams);
    4307         432 :     return CE_None;
    4308             : }

Generated by: LCOV version 1.14