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

Generated by: LCOV version 1.14