• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.5
/alg/gdalgrid.cpp
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

40
constexpr double TO_RADIANS = M_PI / 180.0;
41

42
/************************************************************************/
43
/*                        GDALGridGetPointBounds()                      */
44
/************************************************************************/
45

46
static void GDALGridGetPointBounds(const void *hFeature, CPLRectObj *pBounds)
767,720✔
47
{
48
    const GDALGridPoint *psPoint = static_cast<const GDALGridPoint *>(hFeature);
767,720✔
49
    GDALGridXYArrays *psXYArrays = psPoint->psXYArrays;
767,720✔
50
    const int i = psPoint->i;
767,720✔
51
    const double dfX = psXYArrays->padfX[i];
767,720✔
52
    const double dfY = psXYArrays->padfY[i];
767,720✔
53
    pBounds->minx = dfX;
767,720✔
54
    pBounds->miny = dfY;
767,720✔
55
    pBounds->maxx = dfX;
767,720✔
56
    pBounds->maxy = dfY;
767,720✔
57
}
767,720✔
58

59
/************************************************************************/
60
/*                   GDALGridInverseDistanceToAPower()                  */
61
/************************************************************************/
62

63
/**
64
 * Inverse distance to a power.
65
 *
66
 * The Inverse Distance to a Power gridding method is a weighted average
67
 * interpolator. You should supply the input arrays with the scattered data
68
 * values including coordinates of every data point and output grid geometry.
69
 * The function will compute interpolated value for the given position in
70
 * output grid.
71
 *
72
 * For every grid node the resulting value \f$Z\f$ will be calculated using
73
 * formula:
74
 *
75
 * \f[
76
 *      Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}}
77
 * \f]
78
 *
79
 *  where
80
 *  <ul>
81
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
82
 *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
83
 *           to point \f$i\f$,
84
 *      <li> \f$p\f$ is a weighting power,
85
 *      <li> \f$n\f$ is a total number of points in search ellipse.
86
 *  </ul>
87
 *
88
 *  In this method the weighting factor \f$w\f$ is
89
 *
90
 *  \f[
91
 *      w=\frac{1}{r^p}
92
 *  \f]
93
 *
94
 * @param poOptionsIn Algorithm parameters. This should point to
95
 * GDALGridInverseDistanceToAPowerOptions object.
96
 * @param nPoints Number of elements in input arrays.
97
 * @param padfX Input array of X coordinates.
98
 * @param padfY Input array of Y coordinates.
99
 * @param padfZ Input array of Z values.
100
 * @param dfXPoint X coordinate of the point to compute.
101
 * @param dfYPoint Y coordinate of the point to compute.
102
 * @param pdfValue Pointer to variable where the computed grid node value
103
 * will be returned.
104
 * @param hExtraParamsIn extra parameters (unused)
105
 *
106
 * @return CE_None on success or CE_Failure if something goes wrong.
107
 */
108

109
CPLErr GDALGridInverseDistanceToAPower(const void *poOptionsIn, GUInt32 nPoints,
400✔
110
                                       const double *padfX, const double *padfY,
111
                                       const double *padfZ, double dfXPoint,
112
                                       double dfYPoint, double *pdfValue,
113
                                       CPL_UNUSED void *hExtraParamsIn)
114
{
115
    // TODO: For optimization purposes pre-computed parameters should be moved
116
    // out of this routine to the calling function.
117

118
    const GDALGridInverseDistanceToAPowerOptions *const poOptions =
400✔
119
        static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
120
            poOptionsIn);
121

122
    // Pre-compute search ellipse parameters.
123
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
400✔
124
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
400✔
125
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
400✔
126

127
    // Compute coefficients for coordinate system rotation.
128
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
400✔
129
    const bool bRotated = dfAngle != 0.0;
400✔
130
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
400✔
131
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
400✔
132

133
    const double dfPowerDiv2 = poOptions->dfPower / 2;
400✔
134
    const double dfSmoothing = poOptions->dfSmoothing;
400✔
135
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
400✔
136
    double dfNominator = 0.0;
400✔
137
    double dfDenominator = 0.0;
400✔
138
    GUInt32 n = 0;
400✔
139

140
    for (GUInt32 i = 0; i < nPoints; i++)
160,400✔
141
    {
142
        double dfRX = padfX[i] - dfXPoint;
160,000✔
143
        double dfRY = padfY[i] - dfYPoint;
160,000✔
144
        const double dfR2 =
160,000✔
145
            dfRX * dfRX + dfRY * dfRY + dfSmoothing * dfSmoothing;
160,000✔
146

147
        if (bRotated)
160,000✔
148
        {
149
            const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
×
150
            const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
×
151

152
            dfRX = dfRXRotated;
×
153
            dfRY = dfRYRotated;
×
154
        }
155

156
        // Is this point located inside the search ellipse?
157
        if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
160,000✔
158
            dfR12Square)
159
        {
160
            // If the test point is close to the grid node, use the point
161
            // value directly as a node value to avoid singularity.
162
            if (dfR2 < 0.0000000000001)
3,364✔
163
            {
164
                *pdfValue = padfZ[i];
×
165
                return CE_None;
×
166
            }
167

168
            const double dfW = pow(dfR2, dfPowerDiv2);
3,364✔
169
            const double dfInvW = 1.0 / dfW;
3,364✔
170
            dfNominator += dfInvW * padfZ[i];
3,364✔
171
            dfDenominator += dfInvW;
3,364✔
172
            n++;
3,364✔
173
            if (nMaxPoints > 0 && n > nMaxPoints)
3,364✔
174
                break;
×
175
        }
176
    }
177

178
    if (n < poOptions->nMinPoints || dfDenominator == 0.0)
400✔
179
    {
180
        *pdfValue = poOptions->dfNoDataValue;
76✔
181
    }
182
    else
183
    {
184
        *pdfValue = dfNominator / dfDenominator;
324✔
185
    }
186

187
    return CE_None;
400✔
188
}
189

190
/************************************************************************/
191
/*                   GDALGridInverseDistanceToAPowerNearestNeighbor()   */
192
/************************************************************************/
193

194
/**
195
 * Inverse distance to a power with nearest neighbor search, ideal when
196
 * max_points used.
197
 *
198
 * The Inverse Distance to a Power gridding method is a weighted average
199
 * interpolator. You should supply the input arrays with the scattered data
200
 * values including coordinates of every data point and output grid geometry.
201
 * The function will compute interpolated value for the given position in
202
 * output grid.
203
 *
204
 * For every grid node the resulting value \f$Z\f$ will be calculated using
205
 * formula for nearest matches:
206
 *
207
 * \f[
208
 *      Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}}
209
 * \f]
210
 *
211
 *  where
212
 *  <ul>
213
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
214
 *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
215
 *           to point \f$i\f$ (with an optional smoothing parameter \f$s\f$),
216
 *      <li> \f$p\f$ is a weighting power,
217
 *      <li> \f$n\f$ is a total number of points in search ellipse.
218
 *  </ul>
219
 *
220
 *  In this method the weighting factor \f$w\f$ is
221
 *
222
 *  \f[
223
 *      w=\frac{1}{r^p}
224
 *  \f]
225
 *
226
 * @param poOptionsIn Algorithm parameters. This should point to
227
 * GDALGridInverseDistanceToAPowerNearestNeighborOptions object.
228
 * @param nPoints Number of elements in input arrays.
229
 * @param padfX Input array of X coordinates.
230
 * @param padfY Input array of Y coordinates.
231
 * @param padfZ Input array of Z values.
232
 * @param dfXPoint X coordinate of the point to compute.
233
 * @param dfYPoint Y coordinate of the point to compute.
234
 * @param pdfValue Pointer to variable where the computed grid node value
235
 * will be returned.
236
 * @param hExtraParamsIn extra parameters.
237
 *
238
 * @return CE_None on success or CE_Failure if something goes wrong.
239
 */
240

241
CPLErr GDALGridInverseDistanceToAPowerNearestNeighbor(
1,199✔
242
    const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
243
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
244
    double *pdfValue, void *hExtraParamsIn)
245
{
246
    CPL_IGNORE_RET_VAL(nPoints);
1,199✔
247

248
    const GDALGridInverseDistanceToAPowerNearestNeighborOptions
249
        *const poOptions = static_cast<
1,198✔
250
            const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
251
            poOptionsIn);
252
    const double dfRadius = poOptions->dfRadius;
1,198✔
253
    const double dfSmoothing = poOptions->dfSmoothing;
1,198✔
254
    const double dfSmoothing2 = dfSmoothing * dfSmoothing;
1,198✔
255

256
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
1,198✔
257

258
    GDALGridExtraParameters *psExtraParams =
1,198✔
259
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
260
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
1,198✔
261
    CPLAssert(phQuadTree);
1,198✔
262

263
    const double dfRPower2 = psExtraParams->dfRadiusPower2PreComp;
1,198✔
264
    const double dfPowerDiv2 = psExtraParams->dfPowerDiv2PreComp;
1,198✔
265

266
    std::multimap<double, double> oMapDistanceToZValues;
2,400✔
267

268
    const double dfSearchRadius = dfRadius;
1,200✔
269
    CPLRectObj sAoi;
270
    sAoi.minx = dfXPoint - dfSearchRadius;
1,200✔
271
    sAoi.miny = dfYPoint - dfSearchRadius;
1,200✔
272
    sAoi.maxx = dfXPoint + dfSearchRadius;
1,200✔
273
    sAoi.maxy = dfYPoint + dfSearchRadius;
1,200✔
274
    int nFeatureCount = 0;
1,200✔
275
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
276
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
1,200✔
277
    if (nFeatureCount != 0)
1,201✔
278
    {
279
        for (int k = 0; k < nFeatureCount; k++)
52,076✔
280
        {
281
            const int i = papsPoints[k]->i;
51,058✔
282
            const double dfRX = padfX[i] - dfXPoint;
51,058✔
283
            const double dfRY = padfY[i] - dfYPoint;
51,058✔
284

285
            const double dfR2 = dfRX * dfRX + dfRY * dfRY;
51,058✔
286
            // real distance + smoothing
287
            const double dfRsmoothed2 = dfR2 + dfSmoothing2;
51,058✔
288
            if (dfRsmoothed2 < 0.0000000000001)
51,058✔
289
            {
290
                *pdfValue = padfZ[i];
×
291
                CPLFree(papsPoints);
×
292
                return CE_None;
×
293
            }
294
            // is point within real distance?
295
            if (dfR2 <= dfRPower2)
51,058✔
296
            {
297
                oMapDistanceToZValues.insert(
298
                    std::make_pair(dfRsmoothed2, padfZ[i]));
37,945✔
299
            }
300
        }
301
    }
302
    CPLFree(papsPoints);
1,018✔
303

304
    double dfNominator = 0.0;
1,202✔
305
    double dfDenominator = 0.0;
1,202✔
306
    GUInt32 n = 0;
1,202✔
307

308
    // Examine all "neighbors" within the radius (sorted by distance via the
309
    // multimap), and use the closest n points based on distance until the max
310
    // is reached.
311
    for (std::multimap<double, double>::iterator oMapDistanceToZValuesIter =
8,312✔
312
             oMapDistanceToZValues.begin();
1,202✔
313
         oMapDistanceToZValuesIter != oMapDistanceToZValues.end();
9,514✔
314
         ++oMapDistanceToZValuesIter)
8,304✔
315
    {
316
        const double dfR2 = oMapDistanceToZValuesIter->first;
9,097✔
317
        const double dfZ = oMapDistanceToZValuesIter->second;
9,098✔
318

319
        const double dfW = pow(dfR2, dfPowerDiv2);
9,104✔
320
        const double dfInvW = 1.0 / dfW;
9,104✔
321
        dfNominator += dfInvW * dfZ;
9,104✔
322
        dfDenominator += dfInvW;
9,104✔
323
        n++;
9,104✔
324
        if (nMaxPoints > 0 && n >= nMaxPoints)
9,104✔
325
        {
326
            break;
800✔
327
        }
328
    }
329

330
    if (n < poOptions->nMinPoints || dfDenominator == 0.0)
1,202✔
331
    {
332
        *pdfValue = poOptions->dfNoDataValue;
85✔
333
    }
334
    else
335
    {
336
        *pdfValue = dfNominator / dfDenominator;
1,117✔
337
    }
338

339
    return CE_None;
1,202✔
340
}
341

342
/************************************************************************/
343
/*        GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant()   */
344
/************************************************************************/
345

346
/**
347
 * Inverse distance to a power with nearest neighbor search, with a per-quadrant
348
 * search logic.
349
 */
350
static CPLErr GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant(
6✔
351
    const void *poOptionsIn, GUInt32 /*nPoints*/, const double *padfX,
352
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
353
    double *pdfValue, void *hExtraParamsIn)
354
{
355
    const GDALGridInverseDistanceToAPowerNearestNeighborOptions
356
        *const poOptions = static_cast<
6✔
357
            const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
358
            poOptionsIn);
359
    const double dfRadius = poOptions->dfRadius;
6✔
360
    const double dfSmoothing = poOptions->dfSmoothing;
6✔
361
    const double dfSmoothing2 = dfSmoothing * dfSmoothing;
6✔
362

363
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
6✔
364
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
6✔
365
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
6✔
366

367
    GDALGridExtraParameters *psExtraParams =
6✔
368
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
369
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
6✔
370
    CPLAssert(phQuadTree);
6✔
371

372
    const double dfRPower2 = psExtraParams->dfRadiusPower2PreComp;
6✔
373
    const double dfPowerDiv2 = psExtraParams->dfPowerDiv2PreComp;
6✔
374
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
60✔
375

376
    const double dfSearchRadius = dfRadius;
6✔
377
    CPLRectObj sAoi;
378
    sAoi.minx = dfXPoint - dfSearchRadius;
6✔
379
    sAoi.miny = dfYPoint - dfSearchRadius;
6✔
380
    sAoi.maxx = dfXPoint + dfSearchRadius;
6✔
381
    sAoi.maxy = dfYPoint + dfSearchRadius;
6✔
382
    int nFeatureCount = 0;
6✔
383
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
384
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
6✔
385
    if (nFeatureCount != 0)
6✔
386
    {
387
        for (int k = 0; k < nFeatureCount; k++)
32✔
388
        {
389
            const int i = papsPoints[k]->i;
26✔
390
            const double dfRX = padfX[i] - dfXPoint;
26✔
391
            const double dfRY = padfY[i] - dfYPoint;
26✔
392

393
            const double dfR2 = dfRX * dfRX + dfRY * dfRY;
26✔
394
            // real distance + smoothing
395
            const double dfRsmoothed2 = dfR2 + dfSmoothing2;
26✔
396
            if (dfRsmoothed2 < 0.0000000000001)
26✔
397
            {
398
                *pdfValue = padfZ[i];
×
399
                CPLFree(papsPoints);
×
400
                return CE_None;
×
401
            }
402
            // is point within real distance?
403
            if (dfR2 <= dfRPower2)
26✔
404
            {
405
                const int iQuadrant =
22✔
406
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
22✔
407
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
408
                    std::make_pair(dfRsmoothed2, padfZ[i]));
22✔
409
            }
410
        }
411
    }
412
    CPLFree(papsPoints);
6✔
413

414
    std::multimap<double, double>::iterator aoIter[] = {
415
        oMapDistanceToZValuesPerQuadrant[0].begin(),
6✔
416
        oMapDistanceToZValuesPerQuadrant[1].begin(),
6✔
417
        oMapDistanceToZValuesPerQuadrant[2].begin(),
6✔
418
        oMapDistanceToZValuesPerQuadrant[3].begin(),
6✔
419
    };
6✔
420
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
6✔
421

422
    // Examine all "neighbors" within the radius (sorted by distance via the
423
    // multimap), and use the closest n points based on distance until the max
424
    // is reached.
425
    // Do that by fetching the nearest point in quadrant 0, then the nearest
426
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
427
    // point in quarant 0, etc.
428
    int nQuadrantIterFinishedFlag = 0;
6✔
429
    GUInt32 anPerQuadrant[4] = {0};
6✔
430
    double dfNominator = 0.0;
6✔
431
    double dfDenominator = 0.0;
6✔
432
    GUInt32 n = 0;
6✔
433
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
6✔
434
    {
435
        if (aoIter[iQuadrant] ==
44✔
436
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
98✔
437
            (nMaxPointsPerQuadrant > 0 &&
10✔
438
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
10✔
439
        {
440
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
24✔
441
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
24✔
442
                break;
5✔
443
            continue;
19✔
444
        }
445

446
        const double dfR2 = aoIter[iQuadrant]->first;
20✔
447
        const double dfZ = aoIter[iQuadrant]->second;
20✔
448
        ++aoIter[iQuadrant];
20✔
449

450
        const double dfW = pow(dfR2, dfPowerDiv2);
20✔
451
        const double dfInvW = 1.0 / dfW;
20✔
452
        dfNominator += dfInvW * dfZ;
20✔
453
        dfDenominator += dfInvW;
20✔
454
        n++;
20✔
455
        anPerQuadrant[iQuadrant]++;
20✔
456
        if (nMaxPoints > 0 && n >= nMaxPoints)
20✔
457
        {
458
            break;
1✔
459
        }
460
    }
38✔
461

462
    if (nMinPointsPerQuadrant > 0 &&
6✔
463
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
6✔
464
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
5✔
465
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
4✔
466
         anPerQuadrant[3] < nMinPointsPerQuadrant))
4✔
467
    {
468
        *pdfValue = poOptions->dfNoDataValue;
2✔
469
    }
470
    else if (n < poOptions->nMinPoints || dfDenominator == 0.0)
4✔
471
    {
472
        *pdfValue = poOptions->dfNoDataValue;
1✔
473
    }
474
    else
475
    {
476
        *pdfValue = dfNominator / dfDenominator;
3✔
477
    }
478

479
    return CE_None;
6✔
480
}
481

482
/************************************************************************/
483
/*              GDALGridInverseDistanceToAPowerNoSearch()               */
484
/************************************************************************/
485

486
/**
487
 * Inverse distance to a power for whole data set.
488
 *
489
 * This is somewhat optimized version of the Inverse Distance to a Power
490
 * method. It is used when the search ellips is not set. The algorithm and
491
 * parameters are the same as in GDALGridInverseDistanceToAPower(), but this
492
 * implementation works faster, because of no search.
493
 *
494
 * @see GDALGridInverseDistanceToAPower()
495
 */
496

497
CPLErr GDALGridInverseDistanceToAPowerNoSearch(
501✔
498
    const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
499
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
500
    double *pdfValue, void * /* hExtraParamsIn */)
501
{
502
    const GDALGridInverseDistanceToAPowerOptions *const poOptions =
501✔
503
        static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
504
            poOptionsIn);
505
    const double dfPowerDiv2 = poOptions->dfPower / 2.0;
501✔
506
    const double dfSmoothing = poOptions->dfSmoothing;
501✔
507
    const double dfSmoothing2 = dfSmoothing * dfSmoothing;
501✔
508
    double dfNominator = 0.0;
501✔
509
    double dfDenominator = 0.0;
501✔
510
    const bool bPower2 = dfPowerDiv2 == 1.0;
501✔
511

512
    GUInt32 i = 0;  // Used after if.
501✔
513
    if (bPower2)
501✔
514
    {
515
        if (dfSmoothing2 > 0)
501✔
516
        {
517
            for (i = 0; i < nPoints; i++)
×
518
            {
519
                const double dfRX = padfX[i] - dfXPoint;
×
520
                const double dfRY = padfY[i] - dfYPoint;
×
521
                const double dfR2 = dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
×
522

523
                const double dfInvR2 = 1.0 / dfR2;
×
524
                dfNominator += dfInvR2 * padfZ[i];
×
525
                dfDenominator += dfInvR2;
×
526
            }
527
        }
528
        else
529
        {
530
            for (i = 0; i < nPoints; i++)
80,401✔
531
            {
532
                const double dfRX = padfX[i] - dfXPoint;
80,301✔
533
                const double dfRY = padfY[i] - dfYPoint;
80,301✔
534
                const double dfR2 = dfRX * dfRX + dfRY * dfRY;
80,301✔
535

536
                // If the test point is close to the grid node, use the point
537
                // value directly as a node value to avoid singularity.
538
                if (dfR2 < 0.0000000000001)
80,301✔
539
                {
540
                    break;
401✔
541
                }
542

543
                const double dfInvR2 = 1.0 / dfR2;
79,900✔
544
                dfNominator += dfInvR2 * padfZ[i];
79,900✔
545
                dfDenominator += dfInvR2;
79,900✔
546
            }
547
        }
548
    }
549
    else
550
    {
551
        for (i = 0; i < nPoints; i++)
×
552
        {
553
            const double dfRX = padfX[i] - dfXPoint;
×
554
            const double dfRY = padfY[i] - dfYPoint;
×
555
            const double dfR2 = dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
×
556

557
            // If the test point is close to the grid node, use the point
558
            // value directly as a node value to avoid singularity.
559
            if (dfR2 < 0.0000000000001)
×
560
            {
561
                break;
×
562
            }
563

564
            const double dfW = pow(dfR2, dfPowerDiv2);
×
565
            const double dfInvW = 1.0 / dfW;
×
566
            dfNominator += dfInvW * padfZ[i];
×
567
            dfDenominator += dfInvW;
×
568
        }
569
    }
570

571
    if (i != nPoints)
501✔
572
    {
573
        *pdfValue = padfZ[i];
401✔
574
    }
575
    else if (dfDenominator == 0.0)
100✔
576
    {
577
        *pdfValue = poOptions->dfNoDataValue;
×
578
    }
579
    else
580
    {
581
        *pdfValue = dfNominator / dfDenominator;
100✔
582
    }
583

584
    return CE_None;
501✔
585
}
586

587
/************************************************************************/
588
/*                        GDALGridMovingAverage()                       */
589
/************************************************************************/
590

591
/**
592
 * Moving average.
593
 *
594
 * The Moving Average is a simple data averaging algorithm. It uses a moving
595
 * window of elliptic form to search values and averages all data points
596
 * within the window. Search ellipse can be rotated by specified angle, the
597
 * center of ellipse located at the grid node. Also the minimum number of data
598
 * points to average can be set, if there are not enough points in window, the
599
 * grid node considered empty and will be filled with specified NODATA value.
600
 *
601
 * Mathematically it can be expressed with the formula:
602
 *
603
 * \f[
604
 *      Z=\frac{\sum_{i=1}^n{Z_i}}{n}
605
 * \f]
606
 *
607
 *  where
608
 *  <ul>
609
 *      <li> \f$Z\f$ is a resulting value at the grid node,
610
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
611
 *      <li> \f$n\f$ is a total number of points in search ellipse.
612
 *  </ul>
613
 *
614
 * @param poOptionsIn Algorithm parameters. This should point to
615
 * GDALGridMovingAverageOptions object.
616
 * @param nPoints Number of elements in input arrays.
617
 * @param padfX Input array of X coordinates.
618
 * @param padfY Input array of Y coordinates.
619
 * @param padfZ Input array of Z values.
620
 * @param dfXPoint X coordinate of the point to compute.
621
 * @param dfYPoint Y coordinate of the point to compute.
622
 * @param pdfValue Pointer to variable where the computed grid node value
623
 * will be returned.
624
 * @param hExtraParamsIn extra parameters (unused)
625
 *
626
 * @return CE_None on success or CE_Failure if something goes wrong.
627
 */
628

629
CPLErr GDALGridMovingAverage(const void *poOptionsIn, GUInt32 nPoints,
2,415✔
630
                             const double *padfX, const double *padfY,
631
                             const double *padfZ, double dfXPoint,
632
                             double dfYPoint, double *pdfValue,
633
                             CPL_UNUSED void *hExtraParamsIn)
634
{
635
    // TODO: For optimization purposes pre-computed parameters should be moved
636
    // out of this routine to the calling function.
637

638
    const GDALGridMovingAverageOptions *const poOptions =
2,415✔
639
        static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
640
    // Pre-compute search ellipse parameters.
641
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
2,415✔
642
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
2,415✔
643
    const double dfSearchRadius =
644
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
2,415✔
645
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
2,415✔
646

647
    GDALGridExtraParameters *psExtraParams =
2,415✔
648
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
649
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
2,415✔
650

651
    // Compute coefficients for coordinate system rotation.
652
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
2,415✔
653
    const bool bRotated = dfAngle != 0.0;
2,415✔
654

655
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
2,415✔
656
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
2,415✔
657

658
    double dfAccumulator = 0.0;
2,415✔
659

660
    GUInt32 n = 0;  // Used after for.
2,415✔
661
    if (phQuadTree != nullptr)
2,415✔
662
    {
663
        CPLRectObj sAoi;
664
        sAoi.minx = dfXPoint - dfSearchRadius;
1,600✔
665
        sAoi.miny = dfYPoint - dfSearchRadius;
1,600✔
666
        sAoi.maxx = dfXPoint + dfSearchRadius;
1,600✔
667
        sAoi.maxy = dfYPoint + dfSearchRadius;
1,600✔
668
        int nFeatureCount = 0;
1,600✔
669
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
670
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
1,600✔
671
        if (nFeatureCount != 0)
1,600✔
672
        {
673
            for (int k = 0; k < nFeatureCount; k++)
41,096✔
674
            {
675
                const int i = papsPoints[k]->i;
39,496✔
676
                const double dfRX = padfX[i] - dfXPoint;
39,496✔
677
                const double dfRY = padfY[i] - dfYPoint;
39,496✔
678

679
                if (dfRadius2Square * dfRX * dfRX +
39,496✔
680
                        dfRadius1Square * dfRY * dfRY <=
39,496✔
681
                    dfR12Square)
682
                {
683
                    dfAccumulator += padfZ[i];
32,288✔
684
                    n++;
32,288✔
685
                }
686
            }
687
        }
688
        CPLFree(papsPoints);
1,600✔
689
    }
690
    else
691
    {
692
        for (GUInt32 i = 0; i < nPoints; i++)
320,905✔
693
        {
694
            double dfRX = padfX[i] - dfXPoint;
320,090✔
695
            double dfRY = padfY[i] - dfYPoint;
320,090✔
696

697
            if (bRotated)
320,090✔
698
            {
699
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
160,000✔
700
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
160,000✔
701

702
                dfRX = dfRXRotated;
160,000✔
703
                dfRY = dfRYRotated;
160,000✔
704
            }
705

706
            // Is this point located inside the search ellipse?
707
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
320,090✔
708
                dfR12Square)
709
            {
710
                dfAccumulator += padfZ[i];
169,310✔
711
                n++;
169,310✔
712
            }
713
        }
714
    }
715

716
    if (n < poOptions->nMinPoints || n == 0)
2,415✔
717
    {
718
        *pdfValue = poOptions->dfNoDataValue;
152✔
719
    }
720
    else
721
    {
722
        *pdfValue = dfAccumulator / n;
2,263✔
723
    }
724

725
    return CE_None;
2,415✔
726
}
727

728
/************************************************************************/
729
/*                 GDALGridMovingAveragePerQuadrant()                   */
730
/************************************************************************/
731

732
/**
733
 * Moving average, with a per-quadrant search logic.
734
 */
735
static CPLErr GDALGridMovingAveragePerQuadrant(
6✔
736
    const void *poOptionsIn, GUInt32 /*nPoints*/, const double *padfX,
737
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
738
    double *pdfValue, void *hExtraParamsIn)
739
{
740
    const GDALGridMovingAverageOptions *const poOptions =
6✔
741
        static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
742
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
6✔
743
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
6✔
744
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
6✔
745

746
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
6✔
747
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
6✔
748
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
6✔
749

750
    GDALGridExtraParameters *psExtraParams =
6✔
751
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
752
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
6✔
753
    CPLAssert(phQuadTree);
6✔
754

755
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
60✔
756

757
    const double dfSearchRadius =
758
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
6✔
759
    CPLRectObj sAoi;
760
    sAoi.minx = dfXPoint - dfSearchRadius;
6✔
761
    sAoi.miny = dfYPoint - dfSearchRadius;
6✔
762
    sAoi.maxx = dfXPoint + dfSearchRadius;
6✔
763
    sAoi.maxy = dfYPoint + dfSearchRadius;
6✔
764
    int nFeatureCount = 0;
6✔
765
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
766
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
6✔
767
    if (nFeatureCount != 0)
6✔
768
    {
769
        for (int k = 0; k < nFeatureCount; k++)
32✔
770
        {
771
            const int i = papsPoints[k]->i;
26✔
772
            const double dfRX = padfX[i] - dfXPoint;
26✔
773
            const double dfRY = padfY[i] - dfYPoint;
26✔
774
            const double dfRXSquare = dfRX * dfRX;
26✔
775
            const double dfRYSquare = dfRY * dfRY;
26✔
776

777
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
26✔
778
                dfR12Square)
779
            {
780
                const int iQuadrant =
22✔
781
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
22✔
782
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
783
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
22✔
784
            }
785
        }
786
    }
787
    CPLFree(papsPoints);
6✔
788

789
    std::multimap<double, double>::iterator aoIter[] = {
790
        oMapDistanceToZValuesPerQuadrant[0].begin(),
6✔
791
        oMapDistanceToZValuesPerQuadrant[1].begin(),
6✔
792
        oMapDistanceToZValuesPerQuadrant[2].begin(),
6✔
793
        oMapDistanceToZValuesPerQuadrant[3].begin(),
6✔
794
    };
6✔
795
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
6✔
796

797
    // Examine all "neighbors" within the radius (sorted by distance via the
798
    // multimap), and use the closest n points based on distance until the max
799
    // is reached.
800
    // Do that by fetching the nearest point in quadrant 0, then the nearest
801
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
802
    // point in quarant 0, etc.
803
    int nQuadrantIterFinishedFlag = 0;
6✔
804
    GUInt32 anPerQuadrant[4] = {0};
6✔
805
    double dfNominator = 0.0;
6✔
806
    GUInt32 n = 0;
6✔
807
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
6✔
808
    {
809
        if (aoIter[iQuadrant] ==
44✔
810
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
98✔
811
            (nMaxPointsPerQuadrant > 0 &&
10✔
812
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
10✔
813
        {
814
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
24✔
815
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
24✔
816
                break;
5✔
817
            continue;
19✔
818
        }
819

820
        const double dfZ = aoIter[iQuadrant]->second;
20✔
821
        ++aoIter[iQuadrant];
20✔
822

823
        dfNominator += dfZ;
20✔
824
        n++;
20✔
825
        anPerQuadrant[iQuadrant]++;
20✔
826
        if (nMaxPoints > 0 && n >= nMaxPoints)
20✔
827
        {
828
            break;
1✔
829
        }
830
    }
38✔
831

832
    if (nMinPointsPerQuadrant > 0 &&
6✔
833
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
6✔
834
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
5✔
835
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
4✔
836
         anPerQuadrant[3] < nMinPointsPerQuadrant))
4✔
837
    {
838
        *pdfValue = poOptions->dfNoDataValue;
2✔
839
    }
840
    else if (n < poOptions->nMinPoints || n == 0)
4✔
841
    {
842
        *pdfValue = poOptions->dfNoDataValue;
1✔
843
    }
844
    else
845
    {
846
        *pdfValue = dfNominator / n;
3✔
847
    }
848

849
    return CE_None;
12✔
850
}
851

852
/************************************************************************/
853
/*                        GDALGridNearestNeighbor()                     */
854
/************************************************************************/
855

856
/**
857
 * Nearest neighbor.
858
 *
859
 * The Nearest Neighbor method doesn't perform any interpolation or smoothing,
860
 * it just takes the value of nearest point found in grid node search ellipse
861
 * and returns it as a result. If there are no points found, the specified
862
 * NODATA value will be returned.
863
 *
864
 * @param poOptionsIn Algorithm parameters. This should point to
865
 * GDALGridNearestNeighborOptions object.
866
 * @param nPoints Number of elements in input arrays.
867
 * @param padfX Input array of X coordinates.
868
 * @param padfY Input array of Y coordinates.
869
 * @param padfZ Input array of Z values.
870
 * @param dfXPoint X coordinate of the point to compute.
871
 * @param dfYPoint Y coordinate of the point to compute.
872
 * @param pdfValue Pointer to variable where the computed grid node value
873
 * will be returned.
874
 * @param hExtraParamsIn extra parameters.
875
 *
876
 * @return CE_None on success or CE_Failure if something goes wrong.
877
 */
878

879
CPLErr GDALGridNearestNeighbor(const void *poOptionsIn, GUInt32 nPoints,
44,869✔
880
                               const double *padfX, const double *padfY,
881
                               const double *padfZ, double dfXPoint,
882
                               double dfYPoint, double *pdfValue,
883
                               void *hExtraParamsIn)
884
{
885
    // TODO: For optimization purposes pre-computed parameters should be moved
886
    // out of this routine to the calling function.
887

888
    const GDALGridNearestNeighborOptions *const poOptions =
44,869✔
889
        static_cast<const GDALGridNearestNeighborOptions *>(poOptionsIn);
890
    // Pre-compute search ellipse parameters.
891
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
44,869✔
892
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
44,869✔
893
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
44,869✔
894
    GDALGridExtraParameters *psExtraParams =
44,869✔
895
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
896
    CPLQuadTree *hQuadTree = psExtraParams->hQuadTree;
44,869✔
897

898
    // Compute coefficients for coordinate system rotation.
899
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
44,869✔
900
    const bool bRotated = dfAngle != 0.0;
44,869✔
901
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
44,869✔
902
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
44,869✔
903

904
    // If the nearest point will not be found, its value remains as NODATA.
905
    double dfNearestValue = poOptions->dfNoDataValue;
44,869✔
906
    GUInt32 i = 0;
44,869✔
907

908
    double dfSearchRadius = psExtraParams->dfInitialSearchRadius;
44,869✔
909
    if (hQuadTree != nullptr)
44,869✔
910
    {
911
        if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
14,374✔
912
            dfSearchRadius =
3,199✔
913
                std::max(poOptions->dfRadius1, poOptions->dfRadius2);
3,199✔
914
        CPLRectObj sAoi;
915
        while (dfSearchRadius > 0)
14,978✔
916
        {
917
            sAoi.minx = dfXPoint - dfSearchRadius;
14,979✔
918
            sAoi.miny = dfYPoint - dfSearchRadius;
14,979✔
919
            sAoi.maxx = dfXPoint + dfSearchRadius;
14,979✔
920
            sAoi.maxy = dfYPoint + dfSearchRadius;
14,979✔
921
            int nFeatureCount = 0;
14,979✔
922
            GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
923
                CPLQuadTreeSearch(hQuadTree, &sAoi, &nFeatureCount));
14,979✔
924
            if (nFeatureCount != 0)
14,977✔
925
            {
926
                // Nearest distance will be initialized with the distance to the
927
                // first point in array.
928
                double dfNearestRSquare = std::numeric_limits<double>::max();
14,373✔
929
                for (int k = 0; k < nFeatureCount; k++)
105,019✔
930
                {
931
                    const int idx = papsPoints[k]->i;
90,646✔
932
                    const double dfRX = padfX[idx] - dfXPoint;
90,646✔
933
                    const double dfRY = padfY[idx] - dfYPoint;
90,646✔
934

935
                    const double dfR2 = dfRX * dfRX + dfRY * dfRY;
90,646✔
936
                    if (dfR2 <= dfNearestRSquare)
90,646✔
937
                    {
938
                        dfNearestRSquare = dfR2;
32,454✔
939
                        dfNearestValue = padfZ[idx];
32,454✔
940
                    }
941
                }
942

943
                CPLFree(papsPoints);
14,373✔
944
                break;
14,375✔
945
            }
946

947
            CPLFree(papsPoints);
604✔
948
            if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
604✔
949
                break;
950
            dfSearchRadius *= 2;
604✔
951
#if DEBUG_VERBOSE
952
            CPLDebug("GDAL_GRID", "Increasing search radius to %.16g",
953
                     dfSearchRadius);
954
#endif
955
        }
956
    }
957
    else
958
    {
959
        double dfNearestRSquare = std::numeric_limits<double>::max();
30,495✔
960
        while (i < nPoints)
249,725,000✔
961
        {
962
            double dfRX = padfX[i] - dfXPoint;
249,694,000✔
963
            double dfRY = padfY[i] - dfYPoint;
249,694,000✔
964

965
            if (bRotated)
249,694,000✔
966
            {
967
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
×
968
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
×
969

970
                dfRX = dfRXRotated;
×
971
                dfRY = dfRYRotated;
×
972
            }
973

974
            // Is this point located inside the search ellipse?
975
            const double dfRXSquare = dfRX * dfRX;
249,694,000✔
976
            const double dfRYSquare = dfRY * dfRY;
249,694,000✔
977
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
249,694,000✔
978
                dfR12Square)
979
            {
980
                const double dfR2 = dfRXSquare + dfRYSquare;
240,741,000✔
981
                if (dfR2 <= dfNearestRSquare)
240,741,000✔
982
                {
983
                    dfNearestRSquare = dfR2;
16,112,100✔
984
                    dfNearestValue = padfZ[i];
16,112,100✔
985
                }
986
            }
987

988
            i++;
249,694,000✔
989
        }
990
    }
991

992
    *pdfValue = dfNearestValue;
44,869✔
993

994
    return CE_None;
44,869✔
995
}
996

997
/************************************************************************/
998
/*                      GDALGridDataMetricMinimum()                     */
999
/************************************************************************/
1000

1001
/**
1002
 * Minimum data value (data metric).
1003
 *
1004
 * Minimum value found in grid node search ellipse. If there are no points
1005
 * found, the specified NODATA value will be returned.
1006
 *
1007
 * \f[
1008
 *      Z=\min{(Z_1,Z_2,\ldots,Z_n)}
1009
 * \f]
1010
 *
1011
 *  where
1012
 *  <ul>
1013
 *      <li> \f$Z\f$ is a resulting value at the grid node,
1014
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
1015
 *      <li> \f$n\f$ is a total number of points in search ellipse.
1016
 *  </ul>
1017
 *
1018
 * @param poOptionsIn Algorithm parameters. This should point to
1019
 * GDALGridDataMetricsOptions object.
1020
 * @param nPoints Number of elements in input arrays.
1021
 * @param padfX Input array of X coordinates.
1022
 * @param padfY Input array of Y coordinates.
1023
 * @param padfZ Input array of Z values.
1024
 * @param dfXPoint X coordinate of the point to compute.
1025
 * @param dfYPoint Y coordinate of the point to compute.
1026
 * @param pdfValue Pointer to variable where the computed grid node value
1027
 * will be returned.
1028
 * @param hExtraParamsIn unused.
1029
 *
1030
 * @return CE_None on success or CE_Failure if something goes wrong.
1031
 */
1032

1033
CPLErr GDALGridDataMetricMinimum(const void *poOptionsIn, GUInt32 nPoints,
2,400✔
1034
                                 const double *padfX, const double *padfY,
1035
                                 const double *padfZ, double dfXPoint,
1036
                                 double dfYPoint, double *pdfValue,
1037
                                 void *hExtraParamsIn)
1038
{
1039
    // TODO: For optimization purposes pre-computed parameters should be moved
1040
    // out of this routine to the calling function.
1041

1042
    const GDALGridDataMetricsOptions *const poOptions =
2,400✔
1043
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1044

1045
    // Pre-compute search ellipse parameters.
1046
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
2,400✔
1047
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
2,400✔
1048
    const double dfSearchRadius =
1049
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
2,400✔
1050
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
2,400✔
1051

1052
    GDALGridExtraParameters *psExtraParams =
2,400✔
1053
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1054
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
2,400✔
1055

1056
    // Compute coefficients for coordinate system rotation.
1057
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
2,400✔
1058
    const bool bRotated = dfAngle != 0.0;
2,400✔
1059
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
2,400✔
1060
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
2,400✔
1061

1062
    double dfMinimumValue = std::numeric_limits<double>::max();
2,400✔
1063
    GUInt32 n = 0;
2,400✔
1064
    if (phQuadTree != nullptr)
2,400✔
1065
    {
1066
        CPLRectObj sAoi;
1067
        sAoi.minx = dfXPoint - dfSearchRadius;
1,600✔
1068
        sAoi.miny = dfYPoint - dfSearchRadius;
1,600✔
1069
        sAoi.maxx = dfXPoint + dfSearchRadius;
1,600✔
1070
        sAoi.maxy = dfYPoint + dfSearchRadius;
1,600✔
1071
        int nFeatureCount = 0;
1,600✔
1072
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1073
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
1,600✔
1074
        if (nFeatureCount != 0)
1,600✔
1075
        {
1076
            for (int k = 0; k < nFeatureCount; k++)
35,168✔
1077
            {
1078
                const int i = papsPoints[k]->i;
33,568✔
1079
                const double dfRX = padfX[i] - dfXPoint;
33,568✔
1080
                const double dfRY = padfY[i] - dfYPoint;
33,568✔
1081

1082
                if (dfRadius2Square * dfRX * dfRX +
33,568✔
1083
                        dfRadius1Square * dfRY * dfRY <=
33,568✔
1084
                    dfR12Square)
1085
                {
1086
                    if (dfMinimumValue > padfZ[i])
21,192✔
1087
                        dfMinimumValue = padfZ[i];
3,280✔
1088
                    n++;
21,192✔
1089
                }
1090
            }
1091
        }
1092
        CPLFree(papsPoints);
1,600✔
1093
    }
1094
    else
1095
    {
1096
        GUInt32 i = 0;
800✔
1097
        while (i < nPoints)
320,800✔
1098
        {
1099
            double dfRX = padfX[i] - dfXPoint;
320,000✔
1100
            double dfRY = padfY[i] - dfYPoint;
320,000✔
1101

1102
            if (bRotated)
320,000✔
1103
            {
1104
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
160,000✔
1105
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
160,000✔
1106

1107
                dfRX = dfRXRotated;
160,000✔
1108
                dfRY = dfRYRotated;
160,000✔
1109
            }
1110

1111
            // Is this point located inside the search ellipse?
1112
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
320,000✔
1113
                dfR12Square)
1114
            {
1115
                if (dfMinimumValue > padfZ[i])
171,394✔
1116
                    dfMinimumValue = padfZ[i];
2,873✔
1117
                n++;
171,394✔
1118
            }
1119

1120
            i++;
320,000✔
1121
        }
1122
    }
1123

1124
    if (n < poOptions->nMinPoints || n == 0)
2,400✔
1125
    {
1126
        *pdfValue = poOptions->dfNoDataValue;
×
1127
    }
1128
    else
1129
    {
1130
        *pdfValue = dfMinimumValue;
2,400✔
1131
    }
1132

1133
    return CE_None;
2,400✔
1134
}
1135

1136
/************************************************************************/
1137
/*           GDALGridDataMetricMinimumOrMaximumPerQuadrant()            */
1138
/************************************************************************/
1139

1140
/**
1141
 * Minimum or maximum data value (data metric), with a per-quadrant search
1142
 * logic.
1143
 */
1144
template <bool IS_MIN>
1145
static CPLErr GDALGridDataMetricMinimumOrMaximumPerQuadrant(
10✔
1146
    const void *poOptionsIn, const double *padfX, const double *padfY,
1147
    const double *padfZ, double dfXPoint, double dfYPoint, double *pdfValue,
1148
    void *hExtraParamsIn)
1149
{
1150
    const GDALGridDataMetricsOptions *const poOptions =
10✔
1151
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1152

1153
    // Pre-compute search ellipse parameters.
1154
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
10✔
1155
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
10✔
1156
    const double dfSearchRadius =
10✔
1157
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
10✔
1158
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
10✔
1159

1160
    // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
1161
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
10✔
1162
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
10✔
1163

1164
    GDALGridExtraParameters *psExtraParams =
10✔
1165
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1166
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
10✔
1167
    CPLAssert(phQuadTree);
10✔
1168

1169
    CPLRectObj sAoi;
1170
    sAoi.minx = dfXPoint - dfSearchRadius;
10✔
1171
    sAoi.miny = dfYPoint - dfSearchRadius;
10✔
1172
    sAoi.maxx = dfXPoint + dfSearchRadius;
10✔
1173
    sAoi.maxy = dfYPoint + dfSearchRadius;
10✔
1174
    int nFeatureCount = 0;
10✔
1175
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1176
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
10✔
1177
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
100✔
1178

1179
    if (nFeatureCount != 0)
10✔
1180
    {
1181
        for (int k = 0; k < nFeatureCount; k++)
52✔
1182
        {
1183
            const int i = papsPoints[k]->i;
42✔
1184
            const double dfRX = padfX[i] - dfXPoint;
42✔
1185
            const double dfRY = padfY[i] - dfYPoint;
42✔
1186
            const double dfRXSquare = dfRX * dfRX;
42✔
1187
            const double dfRYSquare = dfRY * dfRY;
42✔
1188

1189
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
42✔
1190
                dfR12Square)
1191
            {
1192
                const int iQuadrant =
34✔
1193
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
34✔
1194
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
34✔
1195
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
68✔
1196
            }
1197
        }
1198
    }
1199
    CPLFree(papsPoints);
10✔
1200

1201
    std::multimap<double, double>::iterator aoIter[] = {
50✔
1202
        oMapDistanceToZValuesPerQuadrant[0].begin(),
10✔
1203
        oMapDistanceToZValuesPerQuadrant[1].begin(),
10✔
1204
        oMapDistanceToZValuesPerQuadrant[2].begin(),
10✔
1205
        oMapDistanceToZValuesPerQuadrant[3].begin(),
10✔
1206
    };
1207
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
10✔
1208

1209
    // Examine all "neighbors" within the radius (sorted by distance via the
1210
    // multimap), and use the closest n points based on distance until the max
1211
    // is reached.
1212
    // Do that by fetching the nearest point in quadrant 0, then the nearest
1213
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
1214
    // point in quarant 0, etc.
1215
    int nQuadrantIterFinishedFlag = 0;
10✔
1216
    GUInt32 anPerQuadrant[4] = {0};
10✔
1217
    double dfExtremum = IS_MIN ? std::numeric_limits<double>::max()
10✔
1218
                               : -std::numeric_limits<double>::max();
1219
    GUInt32 n = 0;
10✔
1220
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
80✔
1221
    {
1222
        if (aoIter[iQuadrant] ==
80✔
1223
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
180✔
1224
            (nMaxPointsPerQuadrant > 0 &&
20✔
1225
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
20✔
1226
        {
1227
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
48✔
1228
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
48✔
1229
                break;
10✔
1230
            continue;
38✔
1231
        }
1232

1233
        const double dfZ = aoIter[iQuadrant]->second;
32✔
1234
        ++aoIter[iQuadrant];
32✔
1235

1236
        if (IS_MIN)
1237
        {
1238
            if (dfExtremum > dfZ)
16✔
1239
                dfExtremum = dfZ;
6✔
1240
        }
1241
        else
1242
        {
1243
            if (dfExtremum < dfZ)
16✔
1244
                dfExtremum = dfZ;
6✔
1245
        }
1246
        n++;
32✔
1247
        anPerQuadrant[iQuadrant]++;
32✔
1248
        /*if( nMaxPoints > 0 && n >= nMaxPoints )
1249
        {
1250
            break;
1251
        }*/
1252
    }
1253

1254
    if (nMinPointsPerQuadrant > 0 &&
10✔
1255
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
10✔
1256
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
8✔
1257
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
6✔
1258
         anPerQuadrant[3] < nMinPointsPerQuadrant))
6✔
1259
    {
1260
        *pdfValue = poOptions->dfNoDataValue;
4✔
1261
    }
1262
    else if (n < poOptions->nMinPoints || n == 0)
6✔
1263
    {
1264
        *pdfValue = poOptions->dfNoDataValue;
2✔
1265
    }
1266
    else
1267
    {
1268
        *pdfValue = dfExtremum;
4✔
1269
    }
1270

1271
    return CE_None;
20✔
1272
}
1273

1274
/************************************************************************/
1275
/*               GDALGridDataMetricMinimumPerQuadrant()                 */
1276
/************************************************************************/
1277

1278
/**
1279
 * Minimum data value (data metric), with a per-quadrant search logic.
1280
 */
1281
static CPLErr GDALGridDataMetricMinimumPerQuadrant(
5✔
1282
    const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
1283
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
1284
    double *pdfValue, void *hExtraParamsIn)
1285
{
1286
    return GDALGridDataMetricMinimumOrMaximumPerQuadrant</*IS_MIN=*/true>(
5✔
1287
        poOptionsIn, padfX, padfY, padfZ, dfXPoint, dfYPoint, pdfValue,
1288
        hExtraParamsIn);
5✔
1289
}
1290

1291
/************************************************************************/
1292
/*                      GDALGridDataMetricMaximum()                     */
1293
/************************************************************************/
1294

1295
/**
1296
 * Maximum data value (data metric).
1297
 *
1298
 * Maximum value found in grid node search ellipse. If there are no points
1299
 * found, the specified NODATA value will be returned.
1300
 *
1301
 * \f[
1302
 *      Z=\max{(Z_1,Z_2,\ldots,Z_n)}
1303
 * \f]
1304
 *
1305
 *  where
1306
 *  <ul>
1307
 *      <li> \f$Z\f$ is a resulting value at the grid node,
1308
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
1309
 *      <li> \f$n\f$ is a total number of points in search ellipse.
1310
 *  </ul>
1311
 *
1312
 * @param poOptionsIn Algorithm parameters. This should point to
1313
 * GDALGridDataMetricsOptions object.
1314
 * @param nPoints Number of elements in input arrays.
1315
 * @param padfX Input array of X coordinates.
1316
 * @param padfY Input array of Y coordinates.
1317
 * @param padfZ Input array of Z values.
1318
 * @param dfXPoint X coordinate of the point to compute.
1319
 * @param dfYPoint Y coordinate of the point to compute.
1320
 * @param pdfValue Pointer to variable where the computed grid node value
1321
 * will be returned.
1322
 * @param hExtraParamsIn extra parameters (unused)
1323
 *
1324
 * @return CE_None on success or CE_Failure if something goes wrong.
1325
 */
1326

1327
CPLErr GDALGridDataMetricMaximum(const void *poOptionsIn, GUInt32 nPoints,
2,400✔
1328
                                 const double *padfX, const double *padfY,
1329
                                 const double *padfZ, double dfXPoint,
1330
                                 double dfYPoint, double *pdfValue,
1331
                                 void *hExtraParamsIn)
1332
{
1333
    // TODO: For optimization purposes pre-computed parameters should be moved
1334
    // out of this routine to the calling function.
1335

1336
    const GDALGridDataMetricsOptions *const poOptions =
2,400✔
1337
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1338

1339
    // Pre-compute search ellipse parameters.
1340
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
2,400✔
1341
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
2,400✔
1342
    const double dfSearchRadius =
1343
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
2,400✔
1344
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
2,400✔
1345

1346
    GDALGridExtraParameters *psExtraParams =
2,400✔
1347
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1348
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
2,400✔
1349

1350
    // Compute coefficients for coordinate system rotation.
1351
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
2,400✔
1352
    const bool bRotated = dfAngle != 0.0;
2,400✔
1353
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
2,400✔
1354
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
2,400✔
1355

1356
    double dfMaximumValue = -std::numeric_limits<double>::max();
2,400✔
1357
    GUInt32 n = 0;
2,400✔
1358
    if (phQuadTree != nullptr)
2,400✔
1359
    {
1360
        CPLRectObj sAoi;
1361
        sAoi.minx = dfXPoint - dfSearchRadius;
1,200✔
1362
        sAoi.miny = dfYPoint - dfSearchRadius;
1,200✔
1363
        sAoi.maxx = dfXPoint + dfSearchRadius;
1,200✔
1364
        sAoi.maxy = dfYPoint + dfSearchRadius;
1,200✔
1365
        int nFeatureCount = 0;
1,200✔
1366
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1367
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
1,200✔
1368
        if (nFeatureCount != 0)
1,200✔
1369
        {
1370
            for (int k = 0; k < nFeatureCount; k++)
37,332✔
1371
            {
1372
                const int i = papsPoints[k]->i;
36,132✔
1373
                const double dfRX = padfX[i] - dfXPoint;
36,132✔
1374
                const double dfRY = padfY[i] - dfYPoint;
36,132✔
1375

1376
                if (dfRadius2Square * dfRX * dfRX +
36,132✔
1377
                        dfRadius1Square * dfRY * dfRY <=
36,132✔
1378
                    dfR12Square)
1379
                {
1380
                    if (dfMaximumValue < padfZ[i])
23,756✔
1381
                        dfMaximumValue = padfZ[i];
3,338✔
1382
                    n++;
23,756✔
1383
                }
1384
            }
1385
        }
1386
        CPLFree(papsPoints);
1,200✔
1387
    }
1388
    else
1389
    {
1390
        GUInt32 i = 0;
1,200✔
1391
        while (i < nPoints)
429,550✔
1392
        {
1393
            double dfRX = padfX[i] - dfXPoint;
428,350✔
1394
            double dfRY = padfY[i] - dfYPoint;
428,350✔
1395

1396
            if (bRotated)
428,350✔
1397
            {
1398
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
272,691✔
1399
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
272,691✔
1400

1401
                dfRX = dfRXRotated;
272,691✔
1402
                dfRY = dfRYRotated;
272,691✔
1403
            }
1404

1405
            // Is this point located inside the search ellipse?
1406
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
428,350✔
1407
                dfR12Square)
1408
            {
1409
                if (dfMaximumValue < padfZ[i])
160,800✔
1410
                    dfMaximumValue = padfZ[i];
4,798✔
1411
                n++;
160,800✔
1412
            }
1413

1414
            i++;
428,350✔
1415
        }
1416
    }
1417

1418
    if (n < poOptions->nMinPoints || n == 0)
2,400✔
1419
    {
1420
        *pdfValue = poOptions->dfNoDataValue;
×
1421
    }
1422
    else
1423
    {
1424
        *pdfValue = dfMaximumValue;
2,400✔
1425
    }
1426

1427
    return CE_None;
2,400✔
1428
}
1429

1430
/************************************************************************/
1431
/*               GDALGridDataMetricMaximumPerQuadrant()                 */
1432
/************************************************************************/
1433

1434
/**
1435
 * Maximum data value (data metric), with a per-quadrant search logic.
1436
 */
1437
static CPLErr GDALGridDataMetricMaximumPerQuadrant(
5✔
1438
    const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
1439
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
1440
    double *pdfValue, void *hExtraParamsIn)
1441
{
1442
    return GDALGridDataMetricMinimumOrMaximumPerQuadrant</*IS_MIN=*/false>(
5✔
1443
        poOptionsIn, padfX, padfY, padfZ, dfXPoint, dfYPoint, pdfValue,
1444
        hExtraParamsIn);
5✔
1445
}
1446

1447
/************************************************************************/
1448
/*                       GDALGridDataMetricRange()                      */
1449
/************************************************************************/
1450

1451
/**
1452
 * Data range (data metric).
1453
 *
1454
 * A difference between the minimum and maximum values found in grid node
1455
 * search ellipse. If there are no points found, the specified NODATA
1456
 * value will be returned.
1457
 *
1458
 * \f[
1459
 *      Z=\max{(Z_1,Z_2,\ldots,Z_n)}-\min{(Z_1,Z_2,\ldots,Z_n)}
1460
 * \f]
1461
 *
1462
 *  where
1463
 *  <ul>
1464
 *      <li> \f$Z\f$ is a resulting value at the grid node,
1465
 *      <li> \f$Z_i\f$ is a known value at point \f$i\f$,
1466
 *      <li> \f$n\f$ is a total number of points in search ellipse.
1467
 *  </ul>
1468
 *
1469
 * @param poOptionsIn Algorithm parameters. This should point to
1470
 * GDALGridDataMetricsOptions object.
1471
 * @param nPoints Number of elements in input arrays.
1472
 * @param padfX Input array of X coordinates.
1473
 * @param padfY Input array of Y coordinates.
1474
 * @param padfZ Input array of Z values.
1475
 * @param dfXPoint X coordinate of the point to compute.
1476
 * @param dfYPoint Y coordinate of the point to compute.
1477
 * @param pdfValue Pointer to variable where the computed grid node value
1478
 * will be returned.
1479
 * @param hExtraParamsIn extra parameters (unused)
1480
 *
1481
 * @return CE_None on success or CE_Failure if something goes wrong.
1482
 */
1483

1484
CPLErr GDALGridDataMetricRange(const void *poOptionsIn, GUInt32 nPoints,
1,200✔
1485
                               const double *padfX, const double *padfY,
1486
                               const double *padfZ, double dfXPoint,
1487
                               double dfYPoint, double *pdfValue,
1488
                               void *hExtraParamsIn)
1489
{
1490
    // TODO: For optimization purposes pre-computed parameters should be moved
1491
    // out of this routine to the calling function.
1492

1493
    const GDALGridDataMetricsOptions *const poOptions =
1,200✔
1494
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1495
    // Pre-compute search ellipse parameters.
1496
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
1,200✔
1497
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
1,200✔
1498
    const double dfSearchRadius =
1499
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
1,200✔
1500
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
1,200✔
1501

1502
    GDALGridExtraParameters *psExtraParams =
1,200✔
1503
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1504
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
1,200✔
1505

1506
    // Compute coefficients for coordinate system rotation.
1507
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
1,200✔
1508
    const bool bRotated = dfAngle != 0.0;
1,200✔
1509
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
1,200✔
1510
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
1,200✔
1511

1512
    double dfMaximumValue = -std::numeric_limits<double>::max();
1,200✔
1513
    double dfMinimumValue = std::numeric_limits<double>::max();
1,200✔
1514
    GUInt32 n = 0;
1,200✔
1515
    if (phQuadTree != nullptr)
1,200✔
1516
    {
1517
        CPLRectObj sAoi;
1518
        sAoi.minx = dfXPoint - dfSearchRadius;
800✔
1519
        sAoi.miny = dfYPoint - dfSearchRadius;
800✔
1520
        sAoi.maxx = dfXPoint + dfSearchRadius;
800✔
1521
        sAoi.maxy = dfYPoint + dfSearchRadius;
800✔
1522
        int nFeatureCount = 0;
800✔
1523
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1524
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
800✔
1525
        if (nFeatureCount != 0)
800✔
1526
        {
1527
            for (int k = 0; k < nFeatureCount; k++)
7,528✔
1528
            {
1529
                const int i = papsPoints[k]->i;
6,728✔
1530
                const double dfRX = padfX[i] - dfXPoint;
6,728✔
1531
                const double dfRY = padfY[i] - dfYPoint;
6,728✔
1532

1533
                if (dfRadius2Square * dfRX * dfRX +
6,728✔
1534
                        dfRadius1Square * dfRY * dfRY <=
6,728✔
1535
                    dfR12Square)
1536
                {
1537
                    if (dfMinimumValue > padfZ[i])
6,728✔
1538
                        dfMinimumValue = padfZ[i];
1,948✔
1539
                    if (dfMaximumValue < padfZ[i])
6,728✔
1540
                        dfMaximumValue = padfZ[i];
1,836✔
1541
                    n++;
6,728✔
1542
                }
1543
            }
1544
        }
1545
        CPLFree(papsPoints);
800✔
1546
    }
1547
    else
1548
    {
1549
        GUInt32 i = 0;
400✔
1550
        while (i < nPoints)
160,400✔
1551
        {
1552
            double dfRX = padfX[i] - dfXPoint;
160,000✔
1553
            double dfRY = padfY[i] - dfYPoint;
160,000✔
1554

1555
            if (bRotated)
160,000✔
1556
            {
1557
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
×
1558
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
×
1559

1560
                dfRX = dfRXRotated;
×
1561
                dfRY = dfRYRotated;
×
1562
            }
1563

1564
            // Is this point located inside the search ellipse?
1565
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
160,000✔
1566
                dfR12Square)
1567
            {
1568
                if (dfMinimumValue > padfZ[i])
160,000✔
1569
                    dfMinimumValue = padfZ[i];
1,600✔
1570
                if (dfMaximumValue < padfZ[i])
160,000✔
1571
                    dfMaximumValue = padfZ[i];
4,000✔
1572
                n++;
160,000✔
1573
            }
1574

1575
            i++;
160,000✔
1576
        }
1577
    }
1578

1579
    if (n < poOptions->nMinPoints || n == 0)
1,200✔
1580
    {
1581
        *pdfValue = poOptions->dfNoDataValue;
152✔
1582
    }
1583
    else
1584
    {
1585
        *pdfValue = dfMaximumValue - dfMinimumValue;
1,048✔
1586
    }
1587

1588
    return CE_None;
1,200✔
1589
}
1590

1591
/************************************************************************/
1592
/*                  GDALGridDataMetricRangePerQuadrant()                */
1593
/************************************************************************/
1594

1595
/**
1596
 * Data range (data metric), with a per-quadrant search logic.
1597
 */
1598
static CPLErr GDALGridDataMetricRangePerQuadrant(
5✔
1599
    const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
1600
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
1601
    double *pdfValue, void *hExtraParamsIn)
1602
{
1603
    const GDALGridDataMetricsOptions *const poOptions =
5✔
1604
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1605

1606
    // Pre-compute search ellipse parameters.
1607
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
5✔
1608
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
5✔
1609
    const double dfSearchRadius =
1610
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
5✔
1611
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
5✔
1612

1613
    // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
1614
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
5✔
1615
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
5✔
1616

1617
    GDALGridExtraParameters *psExtraParams =
5✔
1618
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1619
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
5✔
1620
    CPLAssert(phQuadTree);
5✔
1621

1622
    CPLRectObj sAoi;
1623
    sAoi.minx = dfXPoint - dfSearchRadius;
5✔
1624
    sAoi.miny = dfYPoint - dfSearchRadius;
5✔
1625
    sAoi.maxx = dfXPoint + dfSearchRadius;
5✔
1626
    sAoi.maxy = dfYPoint + dfSearchRadius;
5✔
1627
    int nFeatureCount = 0;
5✔
1628
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1629
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
5✔
1630
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
50✔
1631

1632
    if (nFeatureCount != 0)
5✔
1633
    {
1634
        for (int k = 0; k < nFeatureCount; k++)
26✔
1635
        {
1636
            const int i = papsPoints[k]->i;
21✔
1637
            const double dfRX = padfX[i] - dfXPoint;
21✔
1638
            const double dfRY = padfY[i] - dfYPoint;
21✔
1639
            const double dfRXSquare = dfRX * dfRX;
21✔
1640
            const double dfRYSquare = dfRY * dfRY;
21✔
1641

1642
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
21✔
1643
                dfR12Square)
1644
            {
1645
                const int iQuadrant =
17✔
1646
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
17✔
1647
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
1648
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
17✔
1649
            }
1650
        }
1651
    }
1652
    CPLFree(papsPoints);
5✔
1653

1654
    std::multimap<double, double>::iterator aoIter[] = {
1655
        oMapDistanceToZValuesPerQuadrant[0].begin(),
5✔
1656
        oMapDistanceToZValuesPerQuadrant[1].begin(),
5✔
1657
        oMapDistanceToZValuesPerQuadrant[2].begin(),
5✔
1658
        oMapDistanceToZValuesPerQuadrant[3].begin(),
5✔
1659
    };
5✔
1660
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
5✔
1661

1662
    // Examine all "neighbors" within the radius (sorted by distance via the
1663
    // multimap), and use the closest n points based on distance until the max
1664
    // is reached.
1665
    // Do that by fetching the nearest point in quadrant 0, then the nearest
1666
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
1667
    // point in quarant 0, etc.
1668
    int nQuadrantIterFinishedFlag = 0;
5✔
1669
    GUInt32 anPerQuadrant[4] = {0};
5✔
1670
    double dfMaximumValue = -std::numeric_limits<double>::max();
5✔
1671
    double dfMinimumValue = std::numeric_limits<double>::max();
5✔
1672
    GUInt32 n = 0;
5✔
1673
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
5✔
1674
    {
1675
        if (aoIter[iQuadrant] ==
40✔
1676
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
90✔
1677
            (nMaxPointsPerQuadrant > 0 &&
10✔
1678
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
10✔
1679
        {
1680
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
24✔
1681
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
24✔
1682
                break;
5✔
1683
            continue;
19✔
1684
        }
1685

1686
        const double dfZ = aoIter[iQuadrant]->second;
16✔
1687
        ++aoIter[iQuadrant];
16✔
1688

1689
        if (dfMinimumValue > dfZ)
16✔
1690
            dfMinimumValue = dfZ;
7✔
1691
        if (dfMaximumValue < dfZ)
16✔
1692
            dfMaximumValue = dfZ;
6✔
1693
        n++;
16✔
1694
        anPerQuadrant[iQuadrant]++;
16✔
1695
        /*if( nMaxPoints > 0 && n >= nMaxPoints )
1696
        {
1697
            break;
1698
        }*/
1699
    }
35✔
1700

1701
    if (nMinPointsPerQuadrant > 0 &&
5✔
1702
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
5✔
1703
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
4✔
1704
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
3✔
1705
         anPerQuadrant[3] < nMinPointsPerQuadrant))
3✔
1706
    {
1707
        *pdfValue = poOptions->dfNoDataValue;
2✔
1708
    }
1709
    else if (n < poOptions->nMinPoints || n == 0)
3✔
1710
    {
1711
        *pdfValue = poOptions->dfNoDataValue;
1✔
1712
    }
1713
    else
1714
    {
1715
        *pdfValue = dfMaximumValue - dfMinimumValue;
2✔
1716
    }
1717

1718
    return CE_None;
10✔
1719
}
1720

1721
/************************************************************************/
1722
/*                       GDALGridDataMetricCount()                      */
1723
/************************************************************************/
1724

1725
/**
1726
 * Number of data points (data metric).
1727
 *
1728
 * A number of data points found in grid node search ellipse.
1729
 *
1730
 * \f[
1731
 *      Z=n
1732
 * \f]
1733
 *
1734
 *  where
1735
 *  <ul>
1736
 *      <li> \f$Z\f$ is a resulting value at the grid node,
1737
 *      <li> \f$n\f$ is a total number of points in search ellipse.
1738
 *  </ul>
1739
 *
1740
 * @param poOptionsIn Algorithm parameters. This should point to
1741
 * GDALGridDataMetricsOptions object.
1742
 * @param nPoints Number of elements in input arrays.
1743
 * @param padfX Input array of X coordinates.
1744
 * @param padfY Input array of Y coordinates.
1745
 * @param padfZ Input array of Z values.
1746
 * @param dfXPoint X coordinate of the point to compute.
1747
 * @param dfYPoint Y coordinate of the point to compute.
1748
 * @param pdfValue Pointer to variable where the computed grid node value
1749
 * will be returned.
1750
 * @param hExtraParamsIn extra parameters (unused)
1751
 *
1752
 * @return CE_None on success or CE_Failure if something goes wrong.
1753
 */
1754

1755
CPLErr GDALGridDataMetricCount(const void *poOptionsIn, GUInt32 nPoints,
1,998✔
1756
                               const double *padfX, const double *padfY,
1757
                               CPL_UNUSED const double *padfZ, double dfXPoint,
1758
                               double dfYPoint, double *pdfValue,
1759
                               void *hExtraParamsIn)
1760
{
1761
    // TODO: For optimization purposes pre-computed parameters should be moved
1762
    // out of this routine to the calling function.
1763

1764
    const GDALGridDataMetricsOptions *const poOptions =
1,998✔
1765
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1766

1767
    // Pre-compute search ellipse parameters.
1768
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
1,998✔
1769
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
1,998✔
1770
    const double dfSearchRadius =
1771
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
1,998✔
1772
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
2,000✔
1773

1774
    GDALGridExtraParameters *psExtraParams =
2,000✔
1775
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1776
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
2,000✔
1777

1778
    // Compute coefficients for coordinate system rotation.
1779
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
2,000✔
1780
    const bool bRotated = dfAngle != 0.0;
2,000✔
1781
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
2,000✔
1782
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
2,000✔
1783

1784
    GUInt32 n = 0;
2,000✔
1785
    if (phQuadTree != nullptr)
2,000✔
1786
    {
1787
        CPLRectObj sAoi;
1788
        sAoi.minx = dfXPoint - dfSearchRadius;
2,000✔
1789
        sAoi.miny = dfYPoint - dfSearchRadius;
2,000✔
1790
        sAoi.maxx = dfXPoint + dfSearchRadius;
2,000✔
1791
        sAoi.maxy = dfYPoint + dfSearchRadius;
2,000✔
1792
        int nFeatureCount = 0;
2,000✔
1793
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1794
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
2,000✔
1795
        if (nFeatureCount != 0)
1,999✔
1796
        {
1797
            for (int k = 0; k < nFeatureCount; k++)
81,215✔
1798
            {
1799
                const int i = papsPoints[k]->i;
79,217✔
1800
                const double dfRX = padfX[i] - dfXPoint;
79,217✔
1801
                const double dfRY = padfY[i] - dfYPoint;
79,217✔
1802

1803
                if (dfRadius2Square * dfRX * dfRX +
79,217✔
1804
                        dfRadius1Square * dfRY * dfRY <=
79,217✔
1805
                    dfR12Square)
1806
                {
1807
                    n++;
55,747✔
1808
                }
1809
            }
1810
        }
1811
        CPLFree(papsPoints);
1,999✔
1812
    }
1813
    else
1814
    {
1815
        GUInt32 i = 0;
×
1816
        while (i < nPoints)
×
1817
        {
1818
            double dfRX = padfX[i] - dfXPoint;
×
1819
            double dfRY = padfY[i] - dfYPoint;
×
1820

1821
            if (bRotated)
×
1822
            {
1823
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
×
1824
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
×
1825

1826
                dfRX = dfRXRotated;
×
1827
                dfRY = dfRYRotated;
×
1828
            }
1829

1830
            // Is this point located inside the search ellipse?
1831
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
×
1832
                dfR12Square)
1833
            {
1834
                n++;
×
1835
            }
1836

1837
            i++;
×
1838
        }
1839
    }
1840

1841
    if (n < poOptions->nMinPoints)
1,998✔
1842
    {
1843
        *pdfValue = poOptions->dfNoDataValue;
×
1844
    }
1845
    else
1846
    {
1847
        *pdfValue = static_cast<double>(n);
1,998✔
1848
    }
1849

1850
    return CE_None;
1,998✔
1851
}
1852

1853
/************************************************************************/
1854
/*                  GDALGridDataMetricCountPerQuadrant()                */
1855
/************************************************************************/
1856

1857
/**
1858
 * Number of data points (data metric), with a per-quadrant search logic.
1859
 */
1860
static CPLErr GDALGridDataMetricCountPerQuadrant(
5✔
1861
    const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
1862
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
1863
    double *pdfValue, void *hExtraParamsIn)
1864
{
1865
    const GDALGridDataMetricsOptions *const poOptions =
5✔
1866
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1867

1868
    // Pre-compute search ellipse parameters.
1869
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
5✔
1870
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
5✔
1871
    const double dfSearchRadius =
1872
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
5✔
1873
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
5✔
1874

1875
    // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
1876
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
5✔
1877
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
5✔
1878

1879
    GDALGridExtraParameters *psExtraParams =
5✔
1880
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
1881
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
5✔
1882
    CPLAssert(phQuadTree);
5✔
1883

1884
    CPLRectObj sAoi;
1885
    sAoi.minx = dfXPoint - dfSearchRadius;
5✔
1886
    sAoi.miny = dfYPoint - dfSearchRadius;
5✔
1887
    sAoi.maxx = dfXPoint + dfSearchRadius;
5✔
1888
    sAoi.maxy = dfYPoint + dfSearchRadius;
5✔
1889
    int nFeatureCount = 0;
5✔
1890
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
1891
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
5✔
1892
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
50✔
1893

1894
    if (nFeatureCount != 0)
5✔
1895
    {
1896
        for (int k = 0; k < nFeatureCount; k++)
26✔
1897
        {
1898
            const int i = papsPoints[k]->i;
21✔
1899
            const double dfRX = padfX[i] - dfXPoint;
21✔
1900
            const double dfRY = padfY[i] - dfYPoint;
21✔
1901
            const double dfRXSquare = dfRX * dfRX;
21✔
1902
            const double dfRYSquare = dfRY * dfRY;
21✔
1903

1904
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
21✔
1905
                dfR12Square)
1906
            {
1907
                const int iQuadrant =
17✔
1908
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
17✔
1909
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
1910
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
17✔
1911
            }
1912
        }
1913
    }
1914
    CPLFree(papsPoints);
5✔
1915

1916
    std::multimap<double, double>::iterator aoIter[] = {
1917
        oMapDistanceToZValuesPerQuadrant[0].begin(),
5✔
1918
        oMapDistanceToZValuesPerQuadrant[1].begin(),
5✔
1919
        oMapDistanceToZValuesPerQuadrant[2].begin(),
5✔
1920
        oMapDistanceToZValuesPerQuadrant[3].begin(),
5✔
1921
    };
5✔
1922
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
5✔
1923

1924
    // Examine all "neighbors" within the radius (sorted by distance via the
1925
    // multimap), and use the closest n points based on distance until the max
1926
    // is reached.
1927
    // Do that by fetching the nearest point in quadrant 0, then the nearest
1928
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
1929
    // point in quarant 0, etc.
1930
    int nQuadrantIterFinishedFlag = 0;
5✔
1931
    GUInt32 anPerQuadrant[4] = {0};
5✔
1932
    GUInt32 n = 0;
5✔
1933
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
40✔
1934
    {
1935
        if (aoIter[iQuadrant] ==
40✔
1936
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
90✔
1937
            (nMaxPointsPerQuadrant > 0 &&
10✔
1938
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
10✔
1939
        {
1940
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
24✔
1941
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
24✔
1942
                break;
5✔
1943
            continue;
19✔
1944
        }
1945

1946
        ++aoIter[iQuadrant];
16✔
1947

1948
        n++;
16✔
1949
        anPerQuadrant[iQuadrant]++;
16✔
1950
        /*if( nMaxPoints > 0 && n >= nMaxPoints )
1951
        {
1952
            break;
1953
        }*/
1954
    }
1955

1956
    if (nMinPointsPerQuadrant > 0 &&
5✔
1957
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
5✔
1958
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
4✔
1959
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
3✔
1960
         anPerQuadrant[3] < nMinPointsPerQuadrant))
3✔
1961
    {
1962
        *pdfValue = poOptions->dfNoDataValue;
2✔
1963
    }
1964
    else if (n < poOptions->nMinPoints)
3✔
1965
    {
1966
        *pdfValue = poOptions->dfNoDataValue;
1✔
1967
    }
1968
    else
1969
    {
1970
        *pdfValue = static_cast<double>(n);
2✔
1971
    }
1972

1973
    return CE_None;
10✔
1974
}
1975

1976
/************************************************************************/
1977
/*                 GDALGridDataMetricAverageDistance()                  */
1978
/************************************************************************/
1979

1980
/**
1981
 * Average distance (data metric).
1982
 *
1983
 * An average distance between the grid node (center of the search ellipse)
1984
 * and all of the data points found in grid node search ellipse. If there are
1985
 * no points found, the specified NODATA value will be returned.
1986
 *
1987
 * \f[
1988
 *      Z=\frac{\sum_{i = 1}^n r_i}{n}
1989
 * \f]
1990
 *
1991
 *  where
1992
 *  <ul>
1993
 *      <li> \f$Z\f$ is a resulting value at the grid node,
1994
 *      <li> \f$r_i\f$ is an Euclidean distance from the grid node
1995
 *           to point \f$i\f$,
1996
 *      <li> \f$n\f$ is a total number of points in search ellipse.
1997
 *  </ul>
1998
 *
1999
 * @param poOptionsIn Algorithm parameters. This should point to
2000
 * GDALGridDataMetricsOptions object.
2001
 * @param nPoints Number of elements in input arrays.
2002
 * @param padfX Input array of X coordinates.
2003
 * @param padfY Input array of Y coordinates.
2004
 * @param padfZ Input array of Z values (unused)
2005
 * @param dfXPoint X coordinate of the point to compute.
2006
 * @param dfYPoint Y coordinate of the point to compute.
2007
 * @param pdfValue Pointer to variable where the computed grid node value
2008
 * will be returned.
2009
 * @param hExtraParamsIn extra parameters (unused)
2010
 *
2011
 * @return CE_None on success or CE_Failure if something goes wrong.
2012
 */
2013

2014
CPLErr GDALGridDataMetricAverageDistance(const void *poOptionsIn,
1,200✔
2015
                                         GUInt32 nPoints, const double *padfX,
2016
                                         const double *padfY,
2017
                                         CPL_UNUSED const double *padfZ,
2018
                                         double dfXPoint, double dfYPoint,
2019
                                         double *pdfValue, void *hExtraParamsIn)
2020
{
2021
    // TODO: For optimization purposes pre-computed parameters should be moved
2022
    // out of this routine to the calling function.
2023

2024
    const GDALGridDataMetricsOptions *const poOptions =
1,200✔
2025
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
2026

2027
    // Pre-compute search ellipse parameters.
2028
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
1,200✔
2029
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
1,200✔
2030
    const double dfSearchRadius =
2031
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
1,200✔
2032
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
1,200✔
2033

2034
    GDALGridExtraParameters *psExtraParams =
1,200✔
2035
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
2036
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
1,200✔
2037

2038
    // Compute coefficients for coordinate system rotation.
2039
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
1,200✔
2040
    const bool bRotated = dfAngle != 0.0;
1,200✔
2041
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
1,200✔
2042
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
1,200✔
2043

2044
    double dfAccumulator = 0.0;
1,200✔
2045
    GUInt32 n = 0;
1,200✔
2046
    if (phQuadTree != nullptr)
1,200✔
2047
    {
2048
        CPLRectObj sAoi;
2049
        sAoi.minx = dfXPoint - dfSearchRadius;
800✔
2050
        sAoi.miny = dfYPoint - dfSearchRadius;
800✔
2051
        sAoi.maxx = dfXPoint + dfSearchRadius;
800✔
2052
        sAoi.maxy = dfYPoint + dfSearchRadius;
800✔
2053
        int nFeatureCount = 0;
800✔
2054
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
2055
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
800✔
2056
        if (nFeatureCount != 0)
800✔
2057
        {
2058
            for (int k = 0; k < nFeatureCount; k++)
18,472✔
2059
            {
2060
                const int i = papsPoints[k]->i;
17,672✔
2061
                const double dfRX = padfX[i] - dfXPoint;
17,672✔
2062
                const double dfRY = padfY[i] - dfYPoint;
17,672✔
2063

2064
                if (dfRadius2Square * dfRX * dfRX +
17,672✔
2065
                        dfRadius1Square * dfRY * dfRY <=
17,672✔
2066
                    dfR12Square)
2067
                {
2068
                    dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
15,080✔
2069
                    n++;
15,080✔
2070
                }
2071
            }
2072
        }
2073
        CPLFree(papsPoints);
800✔
2074
    }
2075
    else
2076
    {
2077
        GUInt32 i = 0;
400✔
2078

2079
        while (i < nPoints)
120,039✔
2080
        {
2081
            double dfRX = padfX[i] - dfXPoint;
119,639✔
2082
            double dfRY = padfY[i] - dfYPoint;
119,639✔
2083

2084
            if (bRotated)
119,639✔
2085
            {
2086
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
×
2087
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
×
2088

2089
                dfRX = dfRXRotated;
×
2090
                dfRY = dfRYRotated;
×
2091
            }
2092

2093
            // Is this point located inside the search ellipse?
2094
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
119,639✔
2095
                dfR12Square)
2096
            {
2097
                dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
120,460✔
2098
                n++;
120,460✔
2099
            }
2100

2101
            i++;
119,639✔
2102
        }
2103
    }
2104

2105
    if (n < poOptions->nMinPoints || n == 0)
1,200✔
2106
    {
2107
        *pdfValue = poOptions->dfNoDataValue;
×
2108
    }
2109
    else
2110
    {
2111
        *pdfValue = dfAccumulator / n;
1,200✔
2112
    }
2113

2114
    return CE_None;
1,200✔
2115
}
2116

2117
/************************************************************************/
2118
/*           GDALGridDataMetricAverageDistancePerQuadrant()             */
2119
/************************************************************************/
2120

2121
/**
2122
 * Average distance (data metric), with a per-quadrant search logic.
2123
 */
2124
static CPLErr GDALGridDataMetricAverageDistancePerQuadrant(
5✔
2125
    const void *poOptionsIn, GUInt32 /* nPoints */, const double *padfX,
2126
    const double *padfY, const double *padfZ, double dfXPoint, double dfYPoint,
2127
    double *pdfValue, void *hExtraParamsIn)
2128
{
2129
    const GDALGridDataMetricsOptions *const poOptions =
5✔
2130
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
2131

2132
    // Pre-compute search ellipse parameters.
2133
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
5✔
2134
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
5✔
2135
    const double dfSearchRadius =
2136
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
5✔
2137
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
5✔
2138

2139
    // const GUInt32 nMaxPoints = poOptions->nMaxPoints;
2140
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
5✔
2141
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
5✔
2142

2143
    GDALGridExtraParameters *psExtraParams =
5✔
2144
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
2145
    const CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
5✔
2146
    CPLAssert(phQuadTree);
5✔
2147

2148
    CPLRectObj sAoi;
2149
    sAoi.minx = dfXPoint - dfSearchRadius;
5✔
2150
    sAoi.miny = dfYPoint - dfSearchRadius;
5✔
2151
    sAoi.maxx = dfXPoint + dfSearchRadius;
5✔
2152
    sAoi.maxy = dfYPoint + dfSearchRadius;
5✔
2153
    int nFeatureCount = 0;
5✔
2154
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
2155
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
5✔
2156
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
50✔
2157

2158
    if (nFeatureCount != 0)
5✔
2159
    {
2160
        for (int k = 0; k < nFeatureCount; k++)
26✔
2161
        {
2162
            const int i = papsPoints[k]->i;
21✔
2163
            const double dfRX = padfX[i] - dfXPoint;
21✔
2164
            const double dfRY = padfY[i] - dfYPoint;
21✔
2165
            const double dfRXSquare = dfRX * dfRX;
21✔
2166
            const double dfRYSquare = dfRY * dfRY;
21✔
2167

2168
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
21✔
2169
                dfR12Square)
2170
            {
2171
                const int iQuadrant =
17✔
2172
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
17✔
2173
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
2174
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
17✔
2175
            }
2176
        }
2177
    }
2178
    CPLFree(papsPoints);
5✔
2179

2180
    std::multimap<double, double>::iterator aoIter[] = {
2181
        oMapDistanceToZValuesPerQuadrant[0].begin(),
5✔
2182
        oMapDistanceToZValuesPerQuadrant[1].begin(),
5✔
2183
        oMapDistanceToZValuesPerQuadrant[2].begin(),
5✔
2184
        oMapDistanceToZValuesPerQuadrant[3].begin(),
5✔
2185
    };
5✔
2186
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
5✔
2187

2188
    // Examine all "neighbors" within the radius (sorted by distance via the
2189
    // multimap), and use the closest n points based on distance until the max
2190
    // is reached.
2191
    // Do that by fetching the nearest point in quadrant 0, then the nearest
2192
    // point in quadrant 1, 2 and 3, and starting againg with the next nearest
2193
    // point in quarant 0, etc.
2194
    int nQuadrantIterFinishedFlag = 0;
5✔
2195
    GUInt32 anPerQuadrant[4] = {0};
5✔
2196
    GUInt32 n = 0;
5✔
2197
    double dfAccumulator = 0;
5✔
2198
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
40✔
2199
    {
2200
        if (aoIter[iQuadrant] ==
40✔
2201
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
90✔
2202
            (nMaxPointsPerQuadrant > 0 &&
10✔
2203
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
10✔
2204
        {
2205
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
24✔
2206
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
24✔
2207
                break;
5✔
2208
            continue;
19✔
2209
        }
2210

2211
        dfAccumulator += sqrt(aoIter[iQuadrant]->first);
16✔
2212
        ++aoIter[iQuadrant];
16✔
2213

2214
        n++;
16✔
2215
        anPerQuadrant[iQuadrant]++;
16✔
2216
        /*if( nMaxPoints > 0 && n >= nMaxPoints )
2217
        {
2218
            break;
2219
        }*/
2220
    }
2221

2222
    if (nMinPointsPerQuadrant > 0 &&
5✔
2223
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
5✔
2224
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
4✔
2225
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
3✔
2226
         anPerQuadrant[3] < nMinPointsPerQuadrant))
3✔
2227
    {
2228
        *pdfValue = poOptions->dfNoDataValue;
2✔
2229
    }
2230
    else if (n < poOptions->nMinPoints || n == 0)
3✔
2231
    {
2232
        *pdfValue = poOptions->dfNoDataValue;
1✔
2233
    }
2234
    else
2235
    {
2236
        *pdfValue = dfAccumulator / n;
2✔
2237
    }
2238

2239
    return CE_None;
10✔
2240
}
2241

2242
/************************************************************************/
2243
/*                 GDALGridDataMetricAverageDistancePts()               */
2244
/************************************************************************/
2245

2246
/**
2247
 * Average distance between points (data metric).
2248
 *
2249
 * An average distance between the data points found in grid node search
2250
 * ellipse. The distance between each pair of points within ellipse is
2251
 * calculated and average of all distances is set as a grid node value. If
2252
 * there are no points found, the specified NODATA value will be returned.
2253

2254
 *
2255
 * \f[
2256
 *      Z=\frac{\sum_{i = 1}^{n-1}\sum_{j=i+1}^{n}
2257
 r_{ij}}{\left(n-1\right)\,n-\frac{n+{\left(n-1\right)}^{2}-1}{2}}
2258
 * \f]
2259
 *
2260
 *  where
2261
 *  <ul>
2262
 *      <li> \f$Z\f$ is a resulting value at the grid node,
2263
 *      <li> \f$r_{ij}\f$ is an Euclidean distance between points
2264
 *           \f$i\f$ and \f$j\f$,
2265
 *      <li> \f$n\f$ is a total number of points in search ellipse.
2266
 *  </ul>
2267
 *
2268
 * @param poOptionsIn Algorithm parameters. This should point to
2269
 * GDALGridDataMetricsOptions object.
2270
 * @param nPoints Number of elements in input arrays.
2271
 * @param padfX Input array of X coordinates.
2272
 * @param padfY Input array of Y coordinates.
2273
 * @param padfZ Input array of Z values (unused)
2274
 * @param dfXPoint X coordinate of the point to compute.
2275
 * @param dfYPoint Y coordinate of the point to compute.
2276
 * @param pdfValue Pointer to variable where the computed grid node value
2277
 * will be returned.
2278
 * @param hExtraParamsIn extra parameters (unused)
2279
 *
2280
 * @return CE_None on success or CE_Failure if something goes wrong.
2281
 */
2282

2283
CPLErr GDALGridDataMetricAverageDistancePts(
1,200✔
2284
    const void *poOptionsIn, GUInt32 nPoints, const double *padfX,
2285
    const double *padfY, CPL_UNUSED const double *padfZ, double dfXPoint,
2286
    double dfYPoint, double *pdfValue, void *hExtraParamsIn)
2287
{
2288
    // TODO: For optimization purposes pre-computed parameters should be moved
2289
    // out of this routine to the calling function.
2290

2291
    const GDALGridDataMetricsOptions *const poOptions =
1,200✔
2292
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
2293
    // Pre-compute search ellipse parameters.
2294
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
1,200✔
2295
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
1,200✔
2296
    const double dfSearchRadius =
2297
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
1,200✔
2298
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
1,200✔
2299

2300
    GDALGridExtraParameters *psExtraParams =
1,200✔
2301
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
2302
    CPLQuadTree *phQuadTree = psExtraParams->hQuadTree;
1,200✔
2303

2304
    // Compute coefficients for coordinate system rotation.
2305
    const double dfAngle = TO_RADIANS * poOptions->dfAngle;
1,200✔
2306
    const bool bRotated = dfAngle != 0.0;
1,200✔
2307
    const double dfCoeff1 = bRotated ? cos(dfAngle) : 0.0;
1,200✔
2308
    const double dfCoeff2 = bRotated ? sin(dfAngle) : 0.0;
1,200✔
2309

2310
    double dfAccumulator = 0.0;
1,200✔
2311
    GUInt32 n = 0;
1,200✔
2312
    if (phQuadTree != nullptr)
1,200✔
2313
    {
2314
        CPLRectObj sAoi;
2315
        sAoi.minx = dfXPoint - dfSearchRadius;
800✔
2316
        sAoi.miny = dfYPoint - dfSearchRadius;
800✔
2317
        sAoi.maxx = dfXPoint + dfSearchRadius;
800✔
2318
        sAoi.maxy = dfYPoint + dfSearchRadius;
800✔
2319
        int nFeatureCount = 0;
800✔
2320
        GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
2321
            CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
800✔
2322
        if (nFeatureCount != 0)
800✔
2323
        {
2324
            for (int k = 0; k < nFeatureCount - 1; k++)
17,669✔
2325
            {
2326
                const int i = papsPoints[k]->i;
16,869✔
2327
                const double dfRX1 = padfX[i] - dfXPoint;
16,869✔
2328
                const double dfRY1 = padfY[i] - dfYPoint;
16,869✔
2329

2330
                if (dfRadius2Square * dfRX1 * dfRX1 +
16,869✔
2331
                        dfRadius1Square * dfRY1 * dfRY1 <=
16,869✔
2332
                    dfR12Square)
2333
                {
2334
                    for (int j = k; j < nFeatureCount; j++)
193,455✔
2335
                    // Search all the remaining points within the ellipse and
2336
                    // compute distances between them and the first point.
2337
                    {
2338
                        const int ji = papsPoints[j]->i;
178,828✔
2339
                        double dfRX2 = padfX[ji] - dfXPoint;
178,828✔
2340
                        double dfRY2 = padfY[ji] - dfYPoint;
178,828✔
2341

2342
                        if (dfRadius2Square * dfRX2 * dfRX2 +
178,828✔
2343
                                dfRadius1Square * dfRY2 * dfRY2 <=
178,828✔
2344
                            dfR12Square)
2345
                        {
2346
                            const double dfRX = padfX[ji] - padfX[i];
153,257✔
2347
                            const double dfRY = padfY[ji] - padfY[i];
153,257✔
2348

2349
                            dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
153,257✔
2350
                            n++;
153,257✔
2351
                        }
2352
                    }
2353
                }
2354
            }
2355
        }
2356
        CPLFree(papsPoints);
800✔
2357
    }
2358
    else
2359
    {
2360
        GUInt32 i = 0;
400✔
2361
        while (i < nPoints - 1)
160,000✔
2362
        {
2363
            double dfRX1 = padfX[i] - dfXPoint;
159,600✔
2364
            double dfRY1 = padfY[i] - dfYPoint;
159,600✔
2365

2366
            if (bRotated)
159,600✔
2367
            {
2368
                const double dfRXRotated = dfRX1 * dfCoeff1 + dfRY1 * dfCoeff2;
159,600✔
2369
                const double dfRYRotated = dfRY1 * dfCoeff1 - dfRX1 * dfCoeff2;
159,600✔
2370

2371
                dfRX1 = dfRXRotated;
159,600✔
2372
                dfRY1 = dfRYRotated;
159,600✔
2373
            }
2374

2375
            // Is this point located inside the search ellipse?
2376
            if (dfRadius2Square * dfRX1 * dfRX1 +
159,600✔
2377
                    dfRadius1Square * dfRY1 * dfRY1 <=
159,600✔
2378
                dfR12Square)
2379
            {
2380
                // Search all the remaining points within the ellipse and
2381
                // compute distances between them and the first point.
2382
                for (GUInt32 j = i + 1; j < nPoints; j++)
521,697✔
2383
                {
2384
                    double dfRX2 = padfX[j] - dfXPoint;
519,099✔
2385
                    double dfRY2 = padfY[j] - dfYPoint;
519,099✔
2386

2387
                    if (bRotated)
519,099✔
2388
                    {
2389
                        const double dfRXRotated =
519,099✔
2390
                            dfRX2 * dfCoeff1 + dfRY2 * dfCoeff2;
519,099✔
2391
                        const double dfRYRotated =
519,099✔
2392
                            dfRY2 * dfCoeff1 - dfRX2 * dfCoeff2;
519,099✔
2393

2394
                        dfRX2 = dfRXRotated;
519,099✔
2395
                        dfRY2 = dfRYRotated;
519,099✔
2396
                    }
2397

2398
                    if (dfRadius2Square * dfRX2 * dfRX2 +
519,099✔
2399
                            dfRadius1Square * dfRY2 * dfRY2 <=
519,099✔
2400
                        dfR12Square)
2401
                    {
2402
                        const double dfRX = padfX[j] - padfX[i];
7,342✔
2403
                        const double dfRY = padfY[j] - padfY[i];
7,342✔
2404

2405
                        dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
7,342✔
2406
                        n++;
7,342✔
2407
                    }
2408
                }
2409
            }
2410

2411
            i++;
159,600✔
2412
        }
2413
    }
2414

2415
    // Search for the first point within the search ellipse.
2416
    if (n < poOptions->nMinPoints || n == 0)
1,200✔
2417
    {
2418
        *pdfValue = poOptions->dfNoDataValue;
×
2419
    }
2420
    else
2421
    {
2422
        *pdfValue = dfAccumulator / n;
1,200✔
2423
    }
2424

2425
    return CE_None;
1,200✔
2426
}
2427

2428
/************************************************************************/
2429
/*                        GDALGridLinear()                              */
2430
/************************************************************************/
2431

2432
/**
2433
 * Linear interpolation
2434
 *
2435
 * The Linear method performs linear interpolation by finding in which triangle
2436
 * of a Delaunay triangulation the point is, and by doing interpolation from
2437
 * its barycentric coordinates within the triangle.
2438
 * If the point is not in any triangle, depending on the radius, the
2439
 * algorithm will use the value of the nearest point (radius != 0),
2440
 * or the nodata value (radius == 0)
2441
 *
2442
 * @param poOptionsIn Algorithm parameters. This should point to
2443
 * GDALGridLinearOptions object.
2444
 * @param nPoints Number of elements in input arrays.
2445
 * @param padfX Input array of X coordinates.
2446
 * @param padfY Input array of Y coordinates.
2447
 * @param padfZ Input array of Z values.
2448
 * @param dfXPoint X coordinate of the point to compute.
2449
 * @param dfYPoint Y coordinate of the point to compute.
2450
 * @param pdfValue Pointer to variable where the computed grid node value
2451
 * will be returned.
2452
 * @param hExtraParams extra parameters
2453
 *
2454
 * @return CE_None on success or CE_Failure if something goes wrong.
2455
 *
2456
 * @since GDAL 2.1
2457
 */
2458

2459
CPLErr GDALGridLinear(const void *poOptionsIn, GUInt32 nPoints,
25,336✔
2460
                      const double *padfX, const double *padfY,
2461
                      const double *padfZ, double dfXPoint, double dfYPoint,
2462
                      double *pdfValue, void *hExtraParams)
2463
{
2464
    GDALGridExtraParameters *psExtraParams =
25,336✔
2465
        static_cast<GDALGridExtraParameters *>(hExtraParams);
2466
    GDALTriangulation *psTriangulation = psExtraParams->psTriangulation;
25,336✔
2467

2468
    int nOutputFacetIdx = -1;
25,336✔
2469
    const bool bRet = CPL_TO_BOOL(GDALTriangulationFindFacetDirected(
25,336✔
2470
        psTriangulation, psExtraParams->nInitialFacetIdx, dfXPoint, dfYPoint,
2471
        &nOutputFacetIdx));
2472

2473
    if (bRet)
25,336✔
2474
    {
2475
        CPLAssert(nOutputFacetIdx >= 0);
14,161✔
2476
        // Reuse output facet idx as next initial index since we proceed line by
2477
        // line.
2478
        psExtraParams->nInitialFacetIdx = nOutputFacetIdx;
14,161✔
2479

2480
        double lambda1 = 0.0;
14,161✔
2481
        double lambda2 = 0.0;
14,161✔
2482
        double lambda3 = 0.0;
14,161✔
2483
        GDALTriangulationComputeBarycentricCoordinates(
14,161✔
2484
            psTriangulation, nOutputFacetIdx, dfXPoint, dfYPoint, &lambda1,
2485
            &lambda2, &lambda3);
2486
        const int i1 =
14,161✔
2487
            psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[0];
14,161✔
2488
        const int i2 =
14,161✔
2489
            psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[1];
14,161✔
2490
        const int i3 =
14,161✔
2491
            psTriangulation->pasFacets[nOutputFacetIdx].anVertexIdx[2];
14,161✔
2492
        *pdfValue =
14,161✔
2493
            lambda1 * padfZ[i1] + lambda2 * padfZ[i2] + lambda3 * padfZ[i3];
14,161✔
2494
    }
2495
    else
2496
    {
2497
        if (nOutputFacetIdx >= 0)
11,175✔
2498
        {
2499
            // Also reuse this failed output facet, when valid, as seed for
2500
            // next search.
2501
            psExtraParams->nInitialFacetIdx = nOutputFacetIdx;
480✔
2502
        }
2503

2504
        const GDALGridLinearOptions *const poOptions =
11,175✔
2505
            static_cast<const GDALGridLinearOptions *>(poOptionsIn);
2506
        const double dfRadius = poOptions->dfRadius;
11,175✔
2507
        if (dfRadius == 0.0)
11,175✔
2508
        {
2509
            *pdfValue = poOptions->dfNoDataValue;
×
2510
        }
2511
        else
2512
        {
2513
            GDALGridNearestNeighborOptions sNeighbourOptions;
2514
            sNeighbourOptions.nSizeOfStructure = sizeof(sNeighbourOptions);
11,175✔
2515
            sNeighbourOptions.dfRadius1 = dfRadius < 0.0 ? 0.0 : dfRadius;
11,175✔
2516
            sNeighbourOptions.dfRadius2 = dfRadius < 0.0 ? 0.0 : dfRadius;
11,175✔
2517
            sNeighbourOptions.dfAngle = 0.0;
11,175✔
2518
            sNeighbourOptions.dfNoDataValue = poOptions->dfNoDataValue;
11,175✔
2519
            return GDALGridNearestNeighbor(&sNeighbourOptions, nPoints, padfX,
11,175✔
2520
                                           padfY, padfZ, dfXPoint, dfYPoint,
2521
                                           pdfValue, hExtraParams);
11,175✔
2522
        }
2523
    }
2524

2525
    return CE_None;
14,161✔
2526
}
2527

2528
/************************************************************************/
2529
/*                             GDALGridJob                              */
2530
/************************************************************************/
2531

2532
typedef struct _GDALGridJob GDALGridJob;
2533

2534
struct _GDALGridJob
2535
{
2536
    GUInt32 nYStart;
2537

2538
    GByte *pabyData;
2539
    GUInt32 nYStep;
2540
    GUInt32 nXSize;
2541
    GUInt32 nYSize;
2542
    double dfXMin;
2543
    double dfYMin;
2544
    double dfDeltaX;
2545
    double dfDeltaY;
2546
    GUInt32 nPoints;
2547
    const double *padfX;
2548
    const double *padfY;
2549
    const double *padfZ;
2550
    const void *poOptions;
2551
    GDALGridFunction pfnGDALGridMethod;
2552
    GDALGridExtraParameters *psExtraParameters;
2553
    int (*pfnProgress)(GDALGridJob *psJob);
2554
    GDALDataType eType;
2555

2556
    int *pnCounter;
2557
    volatile int *pbStop;
2558
    CPLCond *hCond;
2559
    CPLMutex *hCondMutex;
2560

2561
    GDALProgressFunc pfnRealProgress;
2562
    void *pRealProgressArg;
2563
};
2564

2565
/************************************************************************/
2566
/*                   GDALGridProgressMultiThread()                      */
2567
/************************************************************************/
2568

2569
// Return TRUE if the computation must be interrupted.
2570
static int GDALGridProgressMultiThread(GDALGridJob *psJob)
1,544✔
2571
{
2572
    CPLAcquireMutex(psJob->hCondMutex, 1.0);
1,544✔
2573
    ++(*psJob->pnCounter);
1,544✔
2574
    CPLCondSignal(psJob->hCond);
1,544✔
2575
    const int bStop = *psJob->pbStop;
1,544✔
2576
    CPLReleaseMutex(psJob->hCondMutex);
1,544✔
2577

2578
    return bStop;
1,544✔
2579
}
2580

2581
/************************************************************************/
2582
/*                      GDALGridProgressMonoThread()                    */
2583
/************************************************************************/
2584

2585
// Return TRUE if the computation must be interrupted.
2586
static int GDALGridProgressMonoThread(GDALGridJob *psJob)
40✔
2587
{
2588
    // coverity[missing_lock]
2589
    const int nCounter = ++(*psJob->pnCounter);
40✔
2590
    if (!psJob->pfnRealProgress(nCounter / static_cast<double>(psJob->nYSize),
40✔
2591
                                "", psJob->pRealProgressArg))
2592
    {
2593
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
×
2594
        *psJob->pbStop = TRUE;
×
2595
        return TRUE;
×
2596
    }
2597
    return FALSE;
40✔
2598
}
2599

2600
/************************************************************************/
2601
/*                         GDALGridJobProcess()                         */
2602
/************************************************************************/
2603

2604
static void GDALGridJobProcess(void *user_data)
426✔
2605
{
2606
    GDALGridJob *const psJob = static_cast<GDALGridJob *>(user_data);
426✔
2607
    int (*pfnProgress)(GDALGridJob * psJob) = psJob->pfnProgress;
426✔
2608
    const GUInt32 nXSize = psJob->nXSize;
426✔
2609

2610
    /* -------------------------------------------------------------------- */
2611
    /*  Allocate a buffer of scanline size, fill it with gridded values     */
2612
    /*  and use GDALCopyWords() to copy values into output data array with  */
2613
    /*  appropriate data type conversion.                                   */
2614
    /* -------------------------------------------------------------------- */
2615
    double *padfValues =
2616
        static_cast<double *>(VSI_MALLOC2_VERBOSE(sizeof(double), nXSize));
426✔
2617
    if (padfValues == nullptr)
426✔
2618
    {
2619
        *(psJob->pbStop) = TRUE;
×
2620
        if (pfnProgress != nullptr)
×
2621
            pfnProgress(psJob);  // To notify the main thread.
×
2622
        return;
×
2623
    }
2624

2625
    const GUInt32 nYStart = psJob->nYStart;
426✔
2626
    const GUInt32 nYStep = psJob->nYStep;
426✔
2627
    GByte *pabyData = psJob->pabyData;
426✔
2628

2629
    const GUInt32 nYSize = psJob->nYSize;
426✔
2630
    const double dfXMin = psJob->dfXMin;
426✔
2631
    const double dfYMin = psJob->dfYMin;
426✔
2632
    const double dfDeltaX = psJob->dfDeltaX;
426✔
2633
    const double dfDeltaY = psJob->dfDeltaY;
426✔
2634
    const GUInt32 nPoints = psJob->nPoints;
426✔
2635
    const double *padfX = psJob->padfX;
426✔
2636
    const double *padfY = psJob->padfY;
426✔
2637
    const double *padfZ = psJob->padfZ;
426✔
2638
    const void *poOptions = psJob->poOptions;
426✔
2639
    GDALGridFunction pfnGDALGridMethod = psJob->pfnGDALGridMethod;
426✔
2640
    // Have a local copy of sExtraParameters since we want to modify
2641
    // nInitialFacetIdx.
2642
    GDALGridExtraParameters sExtraParameters = *psJob->psExtraParameters;
426✔
2643
    const GDALDataType eType = psJob->eType;
426✔
2644

2645
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
426✔
2646
    const int nLineSpace = nXSize * nDataTypeSize;
426✔
2647

2648
    for (GUInt32 nYPoint = nYStart; nYPoint < nYSize; nYPoint += nYStep)
2,010✔
2649
    {
2650
        const double dfYPoint = dfYMin + (nYPoint + 0.5) * dfDeltaY;
1,584✔
2651

2652
        for (GUInt32 nXPoint = 0; nXPoint < nXSize; nXPoint++)
77,392✔
2653
        {
2654
            const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
75,807✔
2655

2656
            if ((*pfnGDALGridMethod)(poOptions, nPoints, padfX, padfY, padfZ,
151,615✔
2657
                                     dfXPoint, dfYPoint, padfValues + nXPoint,
75,807✔
2658
                                     &sExtraParameters) != CE_None)
75,808✔
2659
            {
2660
                CPLError(CE_Failure, CPLE_AppDefined,
×
2661
                         "Gridding failed at X position %lu, Y position %lu",
2662
                         static_cast<long unsigned int>(nXPoint),
2663
                         static_cast<long unsigned int>(nYPoint));
2664
                *psJob->pbStop = TRUE;
×
2665
                if (pfnProgress != nullptr)
×
2666
                    pfnProgress(psJob);  // To notify the main thread.
×
2667
                break;
×
2668
            }
2669
        }
2670

2671
        GDALCopyWords(padfValues, GDT_Float64, sizeof(double),
1,585✔
2672
                      pabyData + nYPoint * nLineSpace, eType, nDataTypeSize,
1,585✔
2673
                      nXSize);
2674

2675
        if (*psJob->pbStop || (pfnProgress != nullptr && pfnProgress(psJob)))
1,584✔
2676
            break;
×
2677
    }
2678

2679
    CPLFree(padfValues);
426✔
2680
}
2681

2682
/************************************************************************/
2683
/*                        GDALGridContextCreate()                       */
2684
/************************************************************************/
2685

2686
struct GDALGridContext
2687
{
2688
    GDALGridAlgorithm eAlgorithm;
2689
    void *poOptions;
2690
    GDALGridFunction pfnGDALGridMethod;
2691

2692
    GUInt32 nPoints;
2693
    GDALGridPoint *pasGridPoints;
2694
    GDALGridXYArrays sXYArrays;
2695

2696
    GDALGridExtraParameters sExtraParameters;
2697
    double *padfX;
2698
    double *padfY;
2699
    double *padfZ;
2700
    bool bFreePadfXYZArrays;
2701

2702
    CPLWorkerThreadPool *poWorkerThreadPool;
2703
};
2704

2705
static void GDALGridContextCreateQuadTree(GDALGridContext *psContext);
2706

2707
/**
2708
 * Creates a context to do regular gridding from the scattered data.
2709
 *
2710
 * This function takes the arrays of X and Y coordinates and corresponding Z
2711
 * values as input to prepare computation of regular grid (or call it a raster)
2712
 * from these scattered data.
2713
 *
2714
 * On Intel/AMD i386/x86_64 architectures, some
2715
 * gridding methods will be optimized with SSE instructions (provided GDAL
2716
 * has been compiled with such support, and it is available at runtime).
2717
 * Currently, only 'invdist' algorithm with default parameters has an optimized
2718
 * implementation.
2719
 * This can provide substantial speed-up, but sometimes at the expense of
2720
 * reduced floating point precision. This can be disabled by setting the
2721
 * GDAL_USE_SSE configuration option to NO.
2722
 * A further optimized version can use the AVX
2723
 * instruction set. This can be disabled by setting the GDAL_USE_AVX
2724
 * configuration option to NO.
2725
 *
2726
 * It is possible to set the GDAL_NUM_THREADS
2727
 * configuration option to parallelize the processing. The value to set is
2728
 * the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the
2729
 * computer (default value).
2730
 *
2731
 * @param eAlgorithm Gridding method.
2732
 * @param poOptions Options to control chosen gridding method.
2733
 * @param nPoints Number of elements in input arrays.
2734
 * @param padfX Input array of X coordinates.
2735
 * @param padfY Input array of Y coordinates.
2736
 * @param padfZ Input array of Z values.
2737
 * @param bCallerWillKeepPointArraysAlive Whether the provided padfX, padfY,
2738
 *        padfZ arrays will still be "alive" during the calls to
2739
 *        GDALGridContextProcess().  Setting to TRUE prevent them from being
2740
 *        duplicated in the context.  If unsure, set to FALSE.
2741
 *
2742
 * @return the context (to be freed with GDALGridContextFree()) or NULL in case
2743
 *         or error.
2744
 *
2745
 * @since GDAL 2.1
2746
 */
2747

2748
GDALGridContext *GDALGridContextCreate(GDALGridAlgorithm eAlgorithm,
108✔
2749
                                       const void *poOptions, GUInt32 nPoints,
2750
                                       const double *padfX, const double *padfY,
2751
                                       const double *padfZ,
2752
                                       int bCallerWillKeepPointArraysAlive)
2753
{
2754
    CPLAssert(poOptions);
108✔
2755
    CPLAssert(padfX);
108✔
2756
    CPLAssert(padfY);
108✔
2757
    CPLAssert(padfZ);
108✔
2758
    bool bCreateQuadTree = false;
108✔
2759

2760
    const unsigned int nPointCountThreshold =
2761
        atoi(CPLGetConfigOption("GDAL_GRID_POINT_COUNT_THRESHOLD", "100"));
108✔
2762

2763
    // Starting address aligned on 32-byte boundary for AVX.
2764
    float *pafXAligned = nullptr;
108✔
2765
    float *pafYAligned = nullptr;
108✔
2766
    float *pafZAligned = nullptr;
108✔
2767

2768
    void *poOptionsNew = nullptr;
108✔
2769

2770
    GDALGridFunction pfnGDALGridMethod = nullptr;
108✔
2771

2772
    switch (eAlgorithm)
108✔
2773
    {
2774
        case GGA_InverseDistanceToAPower:
15✔
2775
        {
2776
            const auto poOptionsOld =
15✔
2777
                static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
2778
                    poOptions);
2779
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
15✔
2780
            {
2781
                CPLError(CE_Failure, CPLE_AppDefined,
×
2782
                         "Wrong value of nSizeOfStructure member");
2783
                return nullptr;
×
2784
            }
2785
            poOptionsNew =
2786
                CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
15✔
2787
            memcpy(poOptionsNew, poOptions,
15✔
2788
                   sizeof(GDALGridInverseDistanceToAPowerOptions));
2789

2790
            const GDALGridInverseDistanceToAPowerOptions *const poPower =
15✔
2791
                static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
2792
                    poOptions);
2793
            if (poPower->dfRadius1 == 0.0 && poPower->dfRadius2 == 0.0)
15✔
2794
            {
2795
                const double dfPower = poPower->dfPower;
14✔
2796
                const double dfSmoothing = poPower->dfSmoothing;
14✔
2797

2798
                pfnGDALGridMethod = GDALGridInverseDistanceToAPowerNoSearch;
14✔
2799
                if (dfPower == 2.0 && dfSmoothing == 0.0)
14✔
2800
                {
2801
#ifdef HAVE_AVX_AT_COMPILE_TIME
2802

2803
                    if (CPLTestBool(
14✔
2804
                            CPLGetConfigOption("GDAL_USE_AVX", "YES")) &&
22✔
2805
                        CPLHaveRuntimeAVX())
8✔
2806
                    {
2807
                        pafXAligned = static_cast<float *>(
2808
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
8✔
2809
                                                            nPoints));
2810
                        pafYAligned = static_cast<float *>(
2811
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
8✔
2812
                                                            nPoints));
2813
                        pafZAligned = static_cast<float *>(
2814
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
8✔
2815
                                                            nPoints));
2816
                        if (pafXAligned != nullptr && pafYAligned != nullptr &&
8✔
2817
                            pafZAligned != nullptr)
2818
                        {
2819
                            CPLDebug("GDAL_GRID",
8✔
2820
                                     "Using AVX optimized version");
2821
                            pfnGDALGridMethod =
8✔
2822
                                GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX;
2823
                            for (GUInt32 i = 0; i < nPoints; i++)
1,224✔
2824
                            {
2825
                                pafXAligned[i] = static_cast<float>(padfX[i]);
1,216✔
2826
                                pafYAligned[i] = static_cast<float>(padfY[i]);
1,216✔
2827
                                pafZAligned[i] = static_cast<float>(padfZ[i]);
1,216✔
2828
                            }
8✔
2829
                        }
2830
                        else
2831
                        {
2832
                            VSIFree(pafXAligned);
×
2833
                            VSIFree(pafYAligned);
×
2834
                            VSIFree(pafZAligned);
×
2835
                            pafXAligned = nullptr;
×
2836
                            pafYAligned = nullptr;
×
2837
                            pafZAligned = nullptr;
×
2838
                        }
2839
                    }
2840
#endif
2841

2842
#ifdef HAVE_SSE_AT_COMPILE_TIME
2843

2844
                    if (pafXAligned == nullptr &&
20✔
2845
                        CPLTestBool(CPLGetConfigOption("GDAL_USE_SSE", "YES"))
6✔
2846
#if !defined(USE_NEON_OPTIMIZATIONS)
2847
                        && CPLHaveRuntimeSSE()
20✔
2848
#endif
2849
                    )
2850
                    {
2851
                        pafXAligned = static_cast<float *>(
2852
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
3✔
2853
                                                            nPoints));
2854
                        pafYAligned = static_cast<float *>(
2855
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
3✔
2856
                                                            nPoints));
2857
                        pafZAligned = static_cast<float *>(
2858
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
3✔
2859
                                                            nPoints));
2860
                        if (pafXAligned != nullptr && pafYAligned != nullptr &&
3✔
2861
                            pafZAligned != nullptr)
2862
                        {
2863
                            CPLDebug("GDAL_GRID",
3✔
2864
                                     "Using SSE optimized version");
2865
                            pfnGDALGridMethod =
3✔
2866
                                GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE;
2867
                            for (GUInt32 i = 0; i < nPoints; i++)
405✔
2868
                            {
2869
                                pafXAligned[i] = static_cast<float>(padfX[i]);
402✔
2870
                                pafYAligned[i] = static_cast<float>(padfY[i]);
402✔
2871
                                pafZAligned[i] = static_cast<float>(padfZ[i]);
402✔
2872
                            }
3✔
2873
                        }
2874
                        else
2875
                        {
2876
                            VSIFree(pafXAligned);
×
2877
                            VSIFree(pafYAligned);
×
2878
                            VSIFree(pafZAligned);
×
2879
                            pafXAligned = nullptr;
×
2880
                            pafYAligned = nullptr;
×
2881
                            pafZAligned = nullptr;
×
2882
                        }
2883
                    }
2884
#endif  // HAVE_SSE_AT_COMPILE_TIME
2885
                }
14✔
2886
            }
2887
            else
2888
            {
2889
                pfnGDALGridMethod = GDALGridInverseDistanceToAPower;
1✔
2890
            }
2891
            break;
15✔
2892
        }
2893
        case GGA_InverseDistanceToAPowerNearestNeighbor:
11✔
2894
        {
2895
            const auto poOptionsOld = static_cast<
11✔
2896
                const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
2897
                poOptions);
2898
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
11✔
2899
            {
2900
                CPLError(CE_Failure, CPLE_AppDefined,
×
2901
                         "Wrong value of nSizeOfStructure member");
2902
                return nullptr;
×
2903
            }
2904
            poOptionsNew = CPLMalloc(
11✔
2905
                sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
2906
            memcpy(
11✔
2907
                poOptionsNew, poOptions,
2908
                sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
2909

2910
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
11✔
2911
                poOptionsOld->nMaxPointsPerQuadrant != 0)
5✔
2912
            {
2913
                pfnGDALGridMethod =
6✔
2914
                    GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant;
2915
            }
2916
            else
2917
            {
2918
                pfnGDALGridMethod =
5✔
2919
                    GDALGridInverseDistanceToAPowerNearestNeighbor;
2920
            }
2921
            bCreateQuadTree = true;
11✔
2922
            break;
11✔
2923
        }
2924
        case GGA_MovingAverage:
13✔
2925
        {
2926
            const auto poOptionsOld =
13✔
2927
                static_cast<const GDALGridMovingAverageOptions *>(poOptions);
2928
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
13✔
2929
            {
2930
                CPLError(CE_Failure, CPLE_AppDefined,
×
2931
                         "Wrong value of nSizeOfStructure member");
2932
                return nullptr;
×
2933
            }
2934
            poOptionsNew = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
13✔
2935
            memcpy(poOptionsNew, poOptions,
13✔
2936
                   sizeof(GDALGridMovingAverageOptions));
2937

2938
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
13✔
2939
                poOptionsOld->nMaxPointsPerQuadrant != 0)
7✔
2940
            {
2941
                pfnGDALGridMethod = GDALGridMovingAveragePerQuadrant;
6✔
2942
                bCreateQuadTree = true;
6✔
2943
            }
2944
            else
2945
            {
2946
                pfnGDALGridMethod = GDALGridMovingAverage;
7✔
2947
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
7✔
2948
                                   poOptionsOld->dfAngle == 0.0 &&
12✔
2949
                                   (poOptionsOld->dfRadius1 > 0.0 ||
5✔
2950
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
2951
            }
2952
            break;
13✔
2953
        }
2954
        case GGA_NearestNeighbor:
16✔
2955
        {
2956
            const auto poOptionsOld =
16✔
2957
                static_cast<const GDALGridNearestNeighborOptions *>(poOptions);
2958
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
16✔
2959
            {
2960
                CPLError(CE_Failure, CPLE_AppDefined,
×
2961
                         "Wrong value of nSizeOfStructure member");
2962
                return nullptr;
×
2963
            }
2964
            poOptionsNew = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
16✔
2965
            memcpy(poOptionsNew, poOptions,
16✔
2966
                   sizeof(GDALGridNearestNeighborOptions));
2967

2968
            pfnGDALGridMethod = GDALGridNearestNeighbor;
16✔
2969
            bCreateQuadTree = (nPoints > nPointCountThreshold &&
29✔
2970
                               poOptionsOld->dfAngle == 0.0 &&
29✔
2971
                               (poOptionsOld->dfRadius1 > 0.0 ||
13✔
2972
                                poOptionsOld->dfRadius2 > 0.0));
5✔
2973
            break;
16✔
2974
        }
2975
        case GGA_MetricMinimum:
11✔
2976
        {
2977
            const auto poOptionsOld =
11✔
2978
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
2979
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
11✔
2980
            {
2981
                CPLError(CE_Failure, CPLE_AppDefined,
×
2982
                         "Wrong value of nSizeOfStructure member");
2983
                return nullptr;
×
2984
            }
2985
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
11✔
2986
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
11✔
2987

2988
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
11✔
2989
                poOptionsOld->nMaxPointsPerQuadrant != 0)
6✔
2990
            {
2991
                pfnGDALGridMethod = GDALGridDataMetricMinimumPerQuadrant;
5✔
2992
                bCreateQuadTree = true;
5✔
2993
            }
2994
            else
2995
            {
2996
                pfnGDALGridMethod = GDALGridDataMetricMinimum;
6✔
2997
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
6✔
2998
                                   poOptionsOld->dfAngle == 0.0 &&
11✔
2999
                                   (poOptionsOld->dfRadius1 > 0.0 ||
5✔
3000
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3001
            }
3002
            break;
11✔
3003
        }
3004
        case GGA_MetricMaximum:
11✔
3005
        {
3006
            const auto poOptionsOld =
11✔
3007
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3008
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
11✔
3009
            {
3010
                CPLError(CE_Failure, CPLE_AppDefined,
×
3011
                         "Wrong value of nSizeOfStructure member");
3012
                return nullptr;
×
3013
            }
3014
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
11✔
3015
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
11✔
3016

3017
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
11✔
3018
                poOptionsOld->nMaxPointsPerQuadrant != 0)
6✔
3019
            {
3020
                pfnGDALGridMethod = GDALGridDataMetricMaximumPerQuadrant;
5✔
3021
                bCreateQuadTree = true;
5✔
3022
            }
3023
            else
3024
            {
3025
                pfnGDALGridMethod = GDALGridDataMetricMaximum;
6✔
3026
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
6✔
3027
                                   poOptionsOld->dfAngle == 0.0 &&
10✔
3028
                                   (poOptionsOld->dfRadius1 > 0.0 ||
4✔
3029
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3030
            }
3031

3032
            break;
11✔
3033
        }
3034
        case GGA_MetricRange:
8✔
3035
        {
3036
            const auto poOptionsOld =
8✔
3037
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3038
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
8✔
3039
            {
3040
                CPLError(CE_Failure, CPLE_AppDefined,
×
3041
                         "Wrong value of nSizeOfStructure member");
3042
                return nullptr;
×
3043
            }
3044
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
8✔
3045
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
8✔
3046

3047
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
8✔
3048
                poOptionsOld->nMaxPointsPerQuadrant != 0)
3✔
3049
            {
3050
                pfnGDALGridMethod = GDALGridDataMetricRangePerQuadrant;
5✔
3051
                bCreateQuadTree = true;
5✔
3052
            }
3053
            else
3054
            {
3055
                pfnGDALGridMethod = GDALGridDataMetricRange;
3✔
3056
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
3✔
3057
                                   poOptionsOld->dfAngle == 0.0 &&
6✔
3058
                                   (poOptionsOld->dfRadius1 > 0.0 ||
3✔
3059
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3060
            }
3061

3062
            break;
8✔
3063
        }
3064
        case GGA_MetricCount:
10✔
3065
        {
3066
            const auto poOptionsOld =
10✔
3067
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3068
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
10✔
3069
            {
3070
                CPLError(CE_Failure, CPLE_AppDefined,
×
3071
                         "Wrong value of nSizeOfStructure member");
3072
                return nullptr;
×
3073
            }
3074
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
10✔
3075
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
10✔
3076

3077
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
10✔
3078
                poOptionsOld->nMaxPointsPerQuadrant != 0)
5✔
3079
            {
3080
                pfnGDALGridMethod = GDALGridDataMetricCountPerQuadrant;
5✔
3081
                bCreateQuadTree = true;
5✔
3082
            }
3083
            else
3084
            {
3085
                pfnGDALGridMethod = GDALGridDataMetricCount;
5✔
3086
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
5✔
3087
                                   poOptionsOld->dfAngle == 0.0 &&
10✔
3088
                                   (poOptionsOld->dfRadius1 > 0.0 ||
5✔
3089
                                    poOptionsOld->dfRadius2 > 0.0));
×
3090
            }
3091

3092
            break;
10✔
3093
        }
3094
        case GGA_MetricAverageDistance:
8✔
3095
        {
3096
            const auto poOptionsOld =
8✔
3097
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3098
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
8✔
3099
            {
3100
                CPLError(CE_Failure, CPLE_AppDefined,
×
3101
                         "Wrong value of nSizeOfStructure member");
3102
                return nullptr;
×
3103
            }
3104
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
8✔
3105
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
8✔
3106

3107
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
8✔
3108
                poOptionsOld->nMaxPointsPerQuadrant != 0)
3✔
3109
            {
3110
                pfnGDALGridMethod =
5✔
3111
                    GDALGridDataMetricAverageDistancePerQuadrant;
3112
                bCreateQuadTree = true;
5✔
3113
            }
3114
            else
3115
            {
3116
                pfnGDALGridMethod = GDALGridDataMetricAverageDistance;
3✔
3117
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
3✔
3118
                                   poOptionsOld->dfAngle == 0.0 &&
6✔
3119
                                   (poOptionsOld->dfRadius1 > 0.0 ||
3✔
3120
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3121
            }
3122

3123
            break;
8✔
3124
        }
3125
        case GGA_MetricAverageDistancePts:
3✔
3126
        {
3127
            const auto poOptionsOld =
3✔
3128
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3129
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
3✔
3130
            {
3131
                CPLError(CE_Failure, CPLE_AppDefined,
×
3132
                         "Wrong value of nSizeOfStructure member");
3133
                return nullptr;
×
3134
            }
3135
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
3✔
3136
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
3✔
3137

3138
            pfnGDALGridMethod = GDALGridDataMetricAverageDistancePts;
3✔
3139
            bCreateQuadTree = (nPoints > nPointCountThreshold &&
6✔
3140
                               poOptionsOld->dfAngle == 0.0 &&
5✔
3141
                               (poOptionsOld->dfRadius1 > 0.0 ||
2✔
3142
                                poOptionsOld->dfRadius2 > 0.0));
×
3143

3144
            break;
3✔
3145
        }
3146
        case GGA_Linear:
2✔
3147
        {
3148
            const auto poOptionsOld =
2✔
3149
                static_cast<const GDALGridLinearOptions *>(poOptions);
3150
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
2✔
3151
            {
3152
                CPLError(CE_Failure, CPLE_AppDefined,
×
3153
                         "Wrong value of nSizeOfStructure member");
3154
                return nullptr;
×
3155
            }
3156
            poOptionsNew = CPLMalloc(sizeof(GDALGridLinearOptions));
2✔
3157
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridLinearOptions));
2✔
3158

3159
            pfnGDALGridMethod = GDALGridLinear;
2✔
3160
            break;
2✔
3161
        }
3162
        default:
×
3163
            CPLError(CE_Failure, CPLE_IllegalArg,
×
3164
                     "GDAL does not support gridding method %d", eAlgorithm);
3165
            return nullptr;
×
3166
    }
3167

3168
    if (pafXAligned == nullptr && !bCallerWillKeepPointArraysAlive)
108✔
3169
    {
3170
        double *padfXNew =
3171
            static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
×
3172
        double *padfYNew =
3173
            static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
×
3174
        double *padfZNew =
3175
            static_cast<double *>(VSI_MALLOC2_VERBOSE(nPoints, sizeof(double)));
×
3176
        if (padfXNew == nullptr || padfYNew == nullptr || padfZNew == nullptr)
×
3177
        {
3178
            VSIFree(padfXNew);
×
3179
            VSIFree(padfYNew);
×
3180
            VSIFree(padfZNew);
×
3181
            CPLFree(poOptionsNew);
×
3182
            return nullptr;
×
3183
        }
3184
        memcpy(padfXNew, padfX, nPoints * sizeof(double));
×
3185
        memcpy(padfYNew, padfY, nPoints * sizeof(double));
×
3186
        memcpy(padfZNew, padfZ, nPoints * sizeof(double));
×
3187
        padfX = padfXNew;
×
3188
        padfY = padfYNew;
×
3189
        padfZ = padfZNew;
×
3190
    }
3191
    GDALGridContext *psContext =
3192
        static_cast<GDALGridContext *>(CPLCalloc(1, sizeof(GDALGridContext)));
108✔
3193
    psContext->eAlgorithm = eAlgorithm;
108✔
3194
    psContext->poOptions = poOptionsNew;
108✔
3195
    psContext->pfnGDALGridMethod = pfnGDALGridMethod;
108✔
3196
    psContext->nPoints = nPoints;
108✔
3197
    psContext->pasGridPoints = nullptr;
108✔
3198
    psContext->sXYArrays.padfX = padfX;
108✔
3199
    psContext->sXYArrays.padfY = padfY;
108✔
3200
    psContext->sExtraParameters.hQuadTree = nullptr;
108✔
3201
    psContext->sExtraParameters.dfInitialSearchRadius = 0.0;
108✔
3202
    psContext->sExtraParameters.pafX = pafXAligned;
108✔
3203
    psContext->sExtraParameters.pafY = pafYAligned;
108✔
3204
    psContext->sExtraParameters.pafZ = pafZAligned;
108✔
3205
    psContext->sExtraParameters.psTriangulation = nullptr;
108✔
3206
    psContext->sExtraParameters.nInitialFacetIdx = 0;
108✔
3207
    psContext->padfX = pafXAligned ? nullptr : const_cast<double *>(padfX);
108✔
3208
    psContext->padfY = pafXAligned ? nullptr : const_cast<double *>(padfY);
108✔
3209
    psContext->padfZ = pafXAligned ? nullptr : const_cast<double *>(padfZ);
108✔
3210
    psContext->bFreePadfXYZArrays =
108✔
3211
        pafXAligned ? false : !bCallerWillKeepPointArraysAlive;
108✔
3212

3213
    /* -------------------------------------------------------------------- */
3214
    /*  Create quadtree if requested and possible.                          */
3215
    /* -------------------------------------------------------------------- */
3216
    if (bCreateQuadTree)
108✔
3217
    {
3218
        GDALGridContextCreateQuadTree(psContext);
72✔
3219
        if (psContext->sExtraParameters.hQuadTree == nullptr &&
72✔
3220
            (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor ||
×
3221
             pfnGDALGridMethod == GDALGridMovingAveragePerQuadrant))
3222
        {
3223
            // shouldn't happen unless memory allocation failure occurs
3224
            GDALGridContextFree(psContext);
×
3225
            return nullptr;
×
3226
        }
3227
    }
3228

3229
    /* -------------------------------------------------------------------- */
3230
    /*  Pre-compute extra parameters in GDALGridExtraParameters              */
3231
    /* -------------------------------------------------------------------- */
3232
    if (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor)
108✔
3233
    {
3234
        const double dfPower =
11✔
3235
            static_cast<
3236
                const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3237
                poOptions)
3238
                ->dfPower;
3239
        psContext->sExtraParameters.dfPowerDiv2PreComp = dfPower / 2;
11✔
3240

3241
        const double dfRadius =
11✔
3242
            static_cast<
3243
                const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3244
                poOptions)
3245
                ->dfRadius;
3246
        psContext->sExtraParameters.dfRadiusPower2PreComp = pow(dfRadius, 2);
11✔
3247
    }
3248

3249
    if (eAlgorithm == GGA_Linear)
108✔
3250
    {
3251
        psContext->sExtraParameters.psTriangulation =
2✔
3252
            GDALTriangulationCreateDelaunay(nPoints, padfX, padfY);
2✔
3253
        if (psContext->sExtraParameters.psTriangulation == nullptr)
2✔
3254
        {
3255
            GDALGridContextFree(psContext);
×
3256
            return nullptr;
×
3257
        }
3258
        GDALTriangulationComputeBarycentricCoefficients(
2✔
3259
            psContext->sExtraParameters.psTriangulation, padfX, padfY);
3260
    }
3261

3262
    /* -------------------------------------------------------------------- */
3263
    /*  Start thread pool.                                                  */
3264
    /* -------------------------------------------------------------------- */
3265
    const char *pszThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
108✔
3266
    int nThreads = 0;
108✔
3267
    if (EQUAL(pszThreads, "ALL_CPUS"))
108✔
3268
        nThreads = CPLGetNumCPUs();
106✔
3269
    else
3270
        nThreads = atoi(pszThreads);
2✔
3271
    if (nThreads > 128)
108✔
3272
        nThreads = 128;
×
3273
    if (nThreads > 1)
108✔
3274
    {
3275
        psContext->poWorkerThreadPool = new CPLWorkerThreadPool();
106✔
3276
        if (!psContext->poWorkerThreadPool->Setup(nThreads, nullptr, nullptr))
106✔
3277
        {
3278
            delete psContext->poWorkerThreadPool;
×
3279
            psContext->poWorkerThreadPool = nullptr;
×
3280
        }
3281
        else
3282
        {
3283
            CPLDebug("GDAL_GRID", "Using %d threads", nThreads);
106✔
3284
        }
3285
    }
3286
    else
3287
        psContext->poWorkerThreadPool = nullptr;
2✔
3288

3289
    return psContext;
108✔
3290
}
3291

3292
/************************************************************************/
3293
/*                      GDALGridContextCreateQuadTree()                 */
3294
/************************************************************************/
3295

3296
void GDALGridContextCreateQuadTree(GDALGridContext *psContext)
74✔
3297
{
3298
    const GUInt32 nPoints = psContext->nPoints;
74✔
3299
    psContext->pasGridPoints = static_cast<GDALGridPoint *>(
74✔
3300
        VSI_MALLOC2_VERBOSE(nPoints, sizeof(GDALGridPoint)));
74✔
3301
    if (psContext->pasGridPoints != nullptr)
74✔
3302
    {
3303
        const double *const padfX = psContext->padfX;
74✔
3304
        const double *const padfY = psContext->padfY;
74✔
3305

3306
        // Determine point extents.
3307
        CPLRectObj sRect;
3308
        sRect.minx = padfX[0];
74✔
3309
        sRect.miny = padfY[0];
74✔
3310
        sRect.maxx = padfX[0];
74✔
3311
        sRect.maxy = padfY[0];
74✔
3312
        for (GUInt32 i = 1; i < nPoints; i++)
28,031✔
3313
        {
3314
            if (padfX[i] < sRect.minx)
27,957✔
3315
                sRect.minx = padfX[i];
40✔
3316
            if (padfY[i] < sRect.miny)
27,957✔
3317
                sRect.miny = padfY[i];
788✔
3318
            if (padfX[i] > sRect.maxx)
27,957✔
3319
                sRect.maxx = padfX[i];
774✔
3320
            if (padfY[i] > sRect.maxy)
27,957✔
3321
                sRect.maxy = padfY[i];
10✔
3322
        }
3323

3324
        // Initial value for search radius is the typical dimension of a
3325
        // "pixel" of the point array (assuming rather uniform distribution).
3326
        psContext->sExtraParameters.dfInitialSearchRadius = sqrt(
74✔
3327
            (sRect.maxx - sRect.minx) * (sRect.maxy - sRect.miny) / nPoints);
74✔
3328

3329
        psContext->sExtraParameters.hQuadTree =
74✔
3330
            CPLQuadTreeCreate(&sRect, GDALGridGetPointBounds);
74✔
3331

3332
        for (GUInt32 i = 0; i < nPoints; i++)
28,105✔
3333
        {
3334
            psContext->pasGridPoints[i].psXYArrays = &(psContext->sXYArrays);
28,031✔
3335
            psContext->pasGridPoints[i].i = i;
28,031✔
3336
            CPLQuadTreeInsert(psContext->sExtraParameters.hQuadTree,
28,031✔
3337
                              psContext->pasGridPoints + i);
28,031✔
3338
        }
3339
    }
3340
}
74✔
3341

3342
/************************************************************************/
3343
/*                        GDALGridContextFree()                         */
3344
/************************************************************************/
3345

3346
/**
3347
 * Free a context used created by GDALGridContextCreate()
3348
 *
3349
 * @param psContext the context.
3350
 *
3351
 * @since GDAL 2.1
3352
 */
3353
void GDALGridContextFree(GDALGridContext *psContext)
108✔
3354
{
3355
    if (psContext)
108✔
3356
    {
3357
        CPLFree(psContext->poOptions);
108✔
3358
        CPLFree(psContext->pasGridPoints);
108✔
3359
        if (psContext->sExtraParameters.hQuadTree != nullptr)
108✔
3360
            CPLQuadTreeDestroy(psContext->sExtraParameters.hQuadTree);
74✔
3361
        if (psContext->bFreePadfXYZArrays)
108✔
3362
        {
3363
            CPLFree(psContext->padfX);
×
3364
            CPLFree(psContext->padfY);
×
3365
            CPLFree(psContext->padfZ);
×
3366
        }
3367
        VSIFreeAligned(psContext->sExtraParameters.pafX);
108✔
3368
        VSIFreeAligned(psContext->sExtraParameters.pafY);
108✔
3369
        VSIFreeAligned(psContext->sExtraParameters.pafZ);
108✔
3370
        if (psContext->sExtraParameters.psTriangulation)
108✔
3371
            GDALTriangulationFree(psContext->sExtraParameters.psTriangulation);
2✔
3372
        delete psContext->poWorkerThreadPool;
108✔
3373
        CPLFree(psContext);
108✔
3374
    }
3375
}
108✔
3376

3377
/************************************************************************/
3378
/*                        GDALGridContextProcess()                      */
3379
/************************************************************************/
3380

3381
/**
3382
 * Do the gridding of a window of a raster.
3383
 *
3384
 * This function takes the gridding context as input to preprare computation
3385
 * of regular grid (or call it a raster) from these scattered data.
3386
 * You should supply the extent of the output grid and allocate array
3387
 * sufficient to hold such a grid.
3388
 *
3389
 * @param psContext Gridding context.
3390
 * @param dfXMin Lowest X border of output grid.
3391
 * @param dfXMax Highest X border of output grid.
3392
 * @param dfYMin Lowest Y border of output grid.
3393
 * @param dfYMax Highest Y border of output grid.
3394
 * @param nXSize Number of columns in output grid.
3395
 * @param nYSize Number of rows in output grid.
3396
 * @param eType Data type of output array.
3397
 * @param pData Pointer to array where the computed grid will be stored.
3398
 * @param pfnProgress a GDALProgressFunc() compatible callback function for
3399
 * reporting progress or NULL.
3400
 * @param pProgressArg argument to be passed to pfnProgress.  May be NULL.
3401
 *
3402
 * @return CE_None on success or CE_Failure if something goes wrong.
3403
 *
3404
 * @since GDAL 2.1
3405
 */
3406

3407
CPLErr GDALGridContextProcess(GDALGridContext *psContext, double dfXMin,
108✔
3408
                              double dfXMax, double dfYMin, double dfYMax,
3409
                              GUInt32 nXSize, GUInt32 nYSize,
3410
                              GDALDataType eType, void *pData,
3411
                              GDALProgressFunc pfnProgress, void *pProgressArg)
3412
{
3413
    CPLAssert(psContext);
108✔
3414
    CPLAssert(pData);
108✔
3415

3416
    if (nXSize == 0 || nYSize == 0)
108✔
3417
    {
3418
        CPLError(CE_Failure, CPLE_IllegalArg,
×
3419
                 "Output raster dimensions should have non-zero size.");
3420
        return CE_Failure;
×
3421
    }
3422

3423
    const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
108✔
3424
    const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
108✔
3425

3426
    // For linear, check if we will need to fallback to nearest neighbour
3427
    // by sampling along the edges.  If all points on edges are within
3428
    // triangles, then interior points will also be.
3429
    if (psContext->eAlgorithm == GGA_Linear &&
108✔
3430
        psContext->sExtraParameters.hQuadTree == nullptr)
2✔
3431
    {
3432
        bool bNeedNearest = false;
2✔
3433
        int nStartLeft = 0;
2✔
3434
        int nStartRight = 0;
2✔
3435
        const double dfXPointMin = dfXMin + (0 + 0.5) * dfDeltaX;
2✔
3436
        const double dfXPointMax = dfXMin + (nXSize - 1 + 0.5) * dfDeltaX;
2✔
3437
        for (GUInt32 nYPoint = 0; !bNeedNearest && nYPoint < nYSize; nYPoint++)
4✔
3438
        {
3439
            const double dfYPoint = dfYMin + (nYPoint + 0.5) * dfDeltaY;
2✔
3440

3441
            if (!GDALTriangulationFindFacetDirected(
2✔
3442
                    psContext->sExtraParameters.psTriangulation, nStartLeft,
2✔
3443
                    dfXPointMin, dfYPoint, &nStartLeft))
3444
            {
3445
                bNeedNearest = true;
2✔
3446
            }
3447
            if (!GDALTriangulationFindFacetDirected(
2✔
3448
                    psContext->sExtraParameters.psTriangulation, nStartRight,
2✔
3449
                    dfXPointMax, dfYPoint, &nStartRight))
3450
            {
3451
                bNeedNearest = true;
2✔
3452
            }
3453
        }
3454
        int nStartTop = 0;
2✔
3455
        int nStartBottom = 0;
2✔
3456
        const double dfYPointMin = dfYMin + (0 + 0.5) * dfDeltaY;
2✔
3457
        const double dfYPointMax = dfYMin + (nYSize - 1 + 0.5) * dfDeltaY;
2✔
3458
        for (GUInt32 nXPoint = 1; !bNeedNearest && nXPoint + 1 < nXSize;
2✔
3459
             nXPoint++)
3460
        {
3461
            const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
×
3462

3463
            if (!GDALTriangulationFindFacetDirected(
×
3464
                    psContext->sExtraParameters.psTriangulation, nStartTop,
×
3465
                    dfXPoint, dfYPointMin, &nStartTop))
3466
            {
3467
                bNeedNearest = true;
×
3468
            }
3469
            if (!GDALTriangulationFindFacetDirected(
×
3470
                    psContext->sExtraParameters.psTriangulation, nStartBottom,
×
3471
                    dfXPoint, dfYPointMax, &nStartBottom))
3472
            {
3473
                bNeedNearest = true;
×
3474
            }
3475
        }
3476
        if (bNeedNearest)
2✔
3477
        {
3478
            CPLDebug("GDAL_GRID", "Will need nearest neighbour");
2✔
3479
            GDALGridContextCreateQuadTree(psContext);
2✔
3480
        }
3481
    }
3482

3483
    int nCounter = 0;
108✔
3484
    volatile int bStop = FALSE;
108✔
3485
    GDALGridJob sJob;
3486
    sJob.nYStart = 0;
108✔
3487
    sJob.pabyData = static_cast<GByte *>(pData);
108✔
3488
    sJob.nYStep = 1;
108✔
3489
    sJob.nXSize = nXSize;
108✔
3490
    sJob.nYSize = nYSize;
108✔
3491
    sJob.dfXMin = dfXMin;
108✔
3492
    sJob.dfYMin = dfYMin;
108✔
3493
    sJob.dfDeltaX = dfDeltaX;
108✔
3494
    sJob.dfDeltaY = dfDeltaY;
108✔
3495
    sJob.nPoints = psContext->nPoints;
108✔
3496
    sJob.padfX = psContext->padfX;
108✔
3497
    sJob.padfY = psContext->padfY;
108✔
3498
    sJob.padfZ = psContext->padfZ;
108✔
3499
    sJob.poOptions = psContext->poOptions;
108✔
3500
    sJob.pfnGDALGridMethod = psContext->pfnGDALGridMethod;
108✔
3501
    sJob.psExtraParameters = &psContext->sExtraParameters;
108✔
3502
    sJob.pfnProgress = nullptr;
108✔
3503
    sJob.eType = eType;
108✔
3504
    sJob.pfnRealProgress = pfnProgress;
108✔
3505
    sJob.pRealProgressArg = pProgressArg;
108✔
3506
    sJob.pnCounter = &nCounter;
108✔
3507
    sJob.pbStop = &bStop;
108✔
3508
    sJob.hCond = nullptr;
108✔
3509
    sJob.hCondMutex = nullptr;
108✔
3510

3511
    if (psContext->poWorkerThreadPool == nullptr)
108✔
3512
    {
3513
        if (sJob.pfnRealProgress != nullptr &&
2✔
3514
            sJob.pfnRealProgress != GDALDummyProgress)
2✔
3515
        {
3516
            sJob.pfnProgress = GDALGridProgressMonoThread;
2✔
3517
        }
3518

3519
        GDALGridJobProcess(&sJob);
2✔
3520
    }
3521
    else
3522
    {
3523
        int nThreads = psContext->poWorkerThreadPool->GetThreadCount();
106✔
3524
        GDALGridJob *pasJobs = static_cast<GDALGridJob *>(
3525
            CPLMalloc(sizeof(GDALGridJob) * nThreads));
106✔
3526

3527
        sJob.nYStep = nThreads;
106✔
3528
        sJob.hCondMutex = CPLCreateMutex(); /* and  implicitly take the mutex */
106✔
3529
        sJob.hCond = CPLCreateCond();
106✔
3530
        sJob.pfnProgress = GDALGridProgressMultiThread;
106✔
3531

3532
        /* --------------------------------------------------------------------
3533
         */
3534
        /*      Start threads. */
3535
        /* --------------------------------------------------------------------
3536
         */
3537
        for (int i = 0; i < nThreads && !bStop; i++)
530✔
3538
        {
3539
            memcpy(&pasJobs[i], &sJob, sizeof(GDALGridJob));
424✔
3540
            pasJobs[i].nYStart = i;
424✔
3541
            psContext->poWorkerThreadPool->SubmitJob(GDALGridJobProcess,
424✔
3542
                                                     &pasJobs[i]);
424✔
3543
        }
3544

3545
        /* --------------------------------------------------------------------
3546
         */
3547
        /*      Report progress. */
3548
        /* --------------------------------------------------------------------
3549
         */
3550
        while (*(sJob.pnCounter) < static_cast<int>(nYSize) && !bStop)
580✔
3551
        {
3552
            CPLCondWait(sJob.hCond, sJob.hCondMutex);
474✔
3553

3554
            int nLocalCounter = *(sJob.pnCounter);
474✔
3555
            CPLReleaseMutex(sJob.hCondMutex);
474✔
3556

3557
            if (pfnProgress != nullptr &&
943✔
3558
                !pfnProgress(nLocalCounter / static_cast<double>(nYSize), "",
469✔
3559
                             pProgressArg))
3560
            {
3561
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
×
3562
                bStop = TRUE;
×
3563
            }
3564

3565
            CPLAcquireMutex(sJob.hCondMutex, 1.0);
474✔
3566
        }
3567

3568
        // Release mutex before joining threads, otherwise they will dead-lock
3569
        // forever in GDALGridProgressMultiThread().
3570
        CPLReleaseMutex(sJob.hCondMutex);
106✔
3571

3572
        /* --------------------------------------------------------------------
3573
         */
3574
        /*      Wait for all threads to complete and finish. */
3575
        /* --------------------------------------------------------------------
3576
         */
3577
        psContext->poWorkerThreadPool->WaitCompletion();
106✔
3578

3579
        CPLFree(pasJobs);
106✔
3580
        CPLDestroyCond(sJob.hCond);
106✔
3581
        CPLDestroyMutex(sJob.hCondMutex);
106✔
3582
    }
3583

3584
    return bStop ? CE_Failure : CE_None;
108✔
3585
}
3586

3587
/************************************************************************/
3588
/*                            GDALGridCreate()                          */
3589
/************************************************************************/
3590

3591
/**
3592
 * Create regular grid from the scattered data.
3593
 *
3594
 * This function takes the arrays of X and Y coordinates and corresponding Z
3595
 * values as input and computes regular grid (or call it a raster) from these
3596
 * scattered data. You should supply geometry and extent of the output grid
3597
 * and allocate array sufficient to hold such a grid.
3598
 *
3599
 * Starting with GDAL 1.10, it is possible to set the GDAL_NUM_THREADS
3600
 * configuration option to parallelize the processing. The value to set is
3601
 * the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the
3602
 * computer (default value).
3603
 *
3604
 * Starting with GDAL 1.10, on Intel/AMD i386/x86_64 architectures, some
3605
 * gridding methods will be optimized with SSE instructions (provided GDAL
3606
 * has been compiled with such support, and it is available at runtime).
3607
 * Currently, only 'invdist' algorithm with default parameters has an optimized
3608
 * implementation.
3609
 * This can provide substantial speed-up, but sometimes at the expense of
3610
 * reduced floating point precision. This can be disabled by setting the
3611
 * GDAL_USE_SSE configuration option to NO.
3612
 * Starting with GDAL 1.11, a further optimized version can use the AVX
3613
 * instruction set. This can be disabled by setting the GDAL_USE_AVX
3614
 * configuration option to NO.
3615
 *
3616
 * Note: it will be more efficient to use GDALGridContextCreate(),
3617
 * GDALGridContextProcess() and GDALGridContextFree() when doing repeated
3618
 * gridding operations with the same algorithm, parameters and points, and
3619
 * moving the window in the output grid.
3620
 *
3621
 * @param eAlgorithm Gridding method.
3622
 * @param poOptions Options to control chosen gridding method.
3623
 * @param nPoints Number of elements in input arrays.
3624
 * @param padfX Input array of X coordinates.
3625
 * @param padfY Input array of Y coordinates.
3626
 * @param padfZ Input array of Z values.
3627
 * @param dfXMin Lowest X border of output grid.
3628
 * @param dfXMax Highest X border of output grid.
3629
 * @param dfYMin Lowest Y border of output grid.
3630
 * @param dfYMax Highest Y border of output grid.
3631
 * @param nXSize Number of columns in output grid.
3632
 * @param nYSize Number of rows in output grid.
3633
 * @param eType Data type of output array.
3634
 * @param pData Pointer to array where the computed grid will be stored.
3635
 * @param pfnProgress a GDALProgressFunc() compatible callback function for
3636
 * reporting progress or NULL.
3637
 * @param pProgressArg argument to be passed to pfnProgress.  May be NULL.
3638
 *
3639
 * @return CE_None on success or CE_Failure if something goes wrong.
3640
 */
3641

3642
CPLErr GDALGridCreate(GDALGridAlgorithm eAlgorithm, const void *poOptions,
5✔
3643
                      GUInt32 nPoints, const double *padfX, const double *padfY,
3644
                      const double *padfZ, double dfXMin, double dfXMax,
3645
                      double dfYMin, double dfYMax, GUInt32 nXSize,
3646
                      GUInt32 nYSize, GDALDataType eType, void *pData,
3647
                      GDALProgressFunc pfnProgress, void *pProgressArg)
3648
{
3649
    GDALGridContext *psContext = GDALGridContextCreate(
5✔
3650
        eAlgorithm, poOptions, nPoints, padfX, padfY, padfZ, TRUE);
3651
    CPLErr eErr = CE_Failure;
5✔
3652
    if (psContext)
5✔
3653
    {
3654
        eErr = GDALGridContextProcess(psContext, dfXMin, dfXMax, dfYMin, dfYMax,
5✔
3655
                                      nXSize, nYSize, eType, pData, pfnProgress,
3656
                                      pProgressArg);
3657
    }
3658

3659
    GDALGridContextFree(psContext);
5✔
3660
    return eErr;
5✔
3661
}
3662

3663
/************************************************************************/
3664
/*                   GDALGridParseAlgorithmAndOptions()                 */
3665
/************************************************************************/
3666

3667
/** Translates mnemonic gridding algorithm names into GDALGridAlgorithm code,
3668
 * parse control parameters and assign defaults.
3669
 */
3670
CPLErr GDALGridParseAlgorithmAndOptions(const char *pszAlgorithm,
229✔
3671
                                        GDALGridAlgorithm *peAlgorithm,
3672
                                        void **ppOptions)
3673
{
3674
    CPLAssert(pszAlgorithm);
229✔
3675
    CPLAssert(peAlgorithm);
229✔
3676
    CPLAssert(ppOptions);
229✔
3677

3678
    *ppOptions = nullptr;
229✔
3679

3680
    char **papszParams = CSLTokenizeString2(pszAlgorithm, ":", FALSE);
229✔
3681

3682
    if (CSLCount(papszParams) < 1)
229✔
3683
    {
3684
        CSLDestroy(papszParams);
×
3685
        return CE_Failure;
×
3686
    }
3687

3688
    if (EQUAL(papszParams[0], szAlgNameInvDist))
229✔
3689
    {
3690
        if (CSLFetchNameValue(papszParams, "min_points_per_quadrant") ||
276✔
3691
            CSLFetchNameValue(papszParams, "max_points_per_quadrant"))
137✔
3692
        {
3693
            // Remap invdist to invdistnn if per quadrant is required
3694
            if (CSLFetchNameValue(papszParams, "radius") == nullptr)
2✔
3695
            {
3696
                const double dfRadius1 =
3697
                    CPLAtofM(CSLFetchNameValueDef(papszParams, "radius1", "1"));
×
3698
                const double dfRadius2 =
3699
                    CPLAtofM(CSLFetchNameValueDef(papszParams, "radius2", "1"));
×
3700
                if (dfRadius1 != dfRadius2)
×
3701
                {
3702
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3703
                             "radius1 != radius2 not supported when "
3704
                             "min_points_per_quadrant and/or "
3705
                             "max_points_per_quadrant is specified");
3706
                    CSLDestroy(papszParams);
×
3707
                    return CE_Failure;
×
3708
                }
3709
            }
3710

3711
            if (CPLAtofM(CSLFetchNameValueDef(papszParams, "angle", "0")) != 0)
2✔
3712
            {
3713
                CPLError(CE_Failure, CPLE_NotSupported,
×
3714
                         "angle != 0 not supported when "
3715
                         "min_points_per_quadrant and/or "
3716
                         "max_points_per_quadrant is specified");
3717
                CSLDestroy(papszParams);
×
3718
                return CE_Failure;
×
3719
            }
3720

3721
            char **papszNewParams = CSLAddString(nullptr, "invdistnn");
2✔
3722
            if (CSLFetchNameValue(papszParams, "radius") == nullptr)
2✔
3723
            {
3724
                papszNewParams = CSLSetNameValue(
×
3725
                    papszNewParams, "radius",
3726
                    CSLFetchNameValueDef(papszParams, "radius1", "1"));
3727
            }
3728
            for (const char *pszItem : {"radius", "power", "smoothing",
12✔
3729
                                        "max_points", "min_points", "nodata"})
14✔
3730
            {
3731
                const char *pszValue = CSLFetchNameValue(papszParams, pszItem);
12✔
3732
                if (pszValue)
12✔
3733
                    papszNewParams =
3734
                        CSLSetNameValue(papszNewParams, pszItem, pszValue);
6✔
3735
            }
3736
            CSLDestroy(papszParams);
2✔
3737
            papszParams = papszNewParams;
2✔
3738

3739
            *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
2✔
3740
        }
3741
        else
3742
        {
3743
            *peAlgorithm = GGA_InverseDistanceToAPower;
137✔
3744
        }
3745
    }
3746
    else if (EQUAL(papszParams[0], szAlgNameInvDistNearestNeighbor))
90✔
3747
    {
3748
        *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
9✔
3749
    }
3750
    else if (EQUAL(papszParams[0], szAlgNameAverage))
81✔
3751
    {
3752
        *peAlgorithm = GGA_MovingAverage;
13✔
3753
    }
3754
    else if (EQUAL(papszParams[0], szAlgNameNearest))
68✔
3755
    {
3756
        *peAlgorithm = GGA_NearestNeighbor;
14✔
3757
    }
3758
    else if (EQUAL(papszParams[0], szAlgNameMinimum))
54✔
3759
    {
3760
        *peAlgorithm = GGA_MetricMinimum;
11✔
3761
    }
3762
    else if (EQUAL(papszParams[0], szAlgNameMaximum))
43✔
3763
    {
3764
        *peAlgorithm = GGA_MetricMaximum;
11✔
3765
    }
3766
    else if (EQUAL(papszParams[0], szAlgNameRange))
32✔
3767
    {
3768
        *peAlgorithm = GGA_MetricRange;
8✔
3769
    }
3770
    else if (EQUAL(papszParams[0], szAlgNameCount))
24✔
3771
    {
3772
        *peAlgorithm = GGA_MetricCount;
10✔
3773
    }
3774
    else if (EQUAL(papszParams[0], szAlgNameAverageDistance))
14✔
3775
    {
3776
        *peAlgorithm = GGA_MetricAverageDistance;
8✔
3777
    }
3778
    else if (EQUAL(papszParams[0], szAlgNameAverageDistancePts))
6✔
3779
    {
3780
        *peAlgorithm = GGA_MetricAverageDistancePts;
3✔
3781
    }
3782
    else if (EQUAL(papszParams[0], szAlgNameLinear))
3✔
3783
    {
3784
        *peAlgorithm = GGA_Linear;
2✔
3785
    }
3786
    else
3787
    {
3788
        CPLError(CE_Failure, CPLE_IllegalArg,
1✔
3789
                 "Unsupported gridding method \"%s\"", papszParams[0]);
3790
        CSLDestroy(papszParams);
1✔
3791
        return CE_Failure;
1✔
3792
    }
3793

3794
    /* -------------------------------------------------------------------- */
3795
    /*      Parse algorithm parameters and assign defaults.                 */
3796
    /* -------------------------------------------------------------------- */
3797
    const char *const *papszKnownOptions = nullptr;
228✔
3798

3799
    switch (*peAlgorithm)
228✔
3800
    {
3801
        case GGA_InverseDistanceToAPower:
137✔
3802
        default:
3803
        {
3804
            *ppOptions =
137✔
3805
                CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
137✔
3806

3807
            GDALGridInverseDistanceToAPowerOptions *const poPowerOpts =
137✔
3808
                static_cast<GDALGridInverseDistanceToAPowerOptions *>(
3809
                    *ppOptions);
3810

3811
            poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
137✔
3812

3813
            const char *pszValue = CSLFetchNameValue(papszParams, "power");
137✔
3814
            poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
137✔
3815

3816
            pszValue = CSLFetchNameValue(papszParams, "smoothing");
137✔
3817
            poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
137✔
3818

3819
            pszValue = CSLFetchNameValue(papszParams, "radius");
137✔
3820
            if (pszValue)
137✔
3821
            {
3822
                poPowerOpts->dfRadius1 = CPLAtofM(pszValue);
×
3823
                poPowerOpts->dfRadius2 = poPowerOpts->dfRadius1;
×
3824
            }
3825
            else
3826
            {
3827
                pszValue = CSLFetchNameValue(papszParams, "radius1");
137✔
3828
                poPowerOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
137✔
3829

3830
                pszValue = CSLFetchNameValue(papszParams, "radius2");
137✔
3831
                poPowerOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
137✔
3832
            }
3833

3834
            pszValue = CSLFetchNameValue(papszParams, "angle");
137✔
3835
            poPowerOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
137✔
3836

3837
            pszValue = CSLFetchNameValue(papszParams, "max_points");
137✔
3838
            poPowerOpts->nMaxPoints =
137✔
3839
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
137✔
3840

3841
            pszValue = CSLFetchNameValue(papszParams, "min_points");
137✔
3842
            poPowerOpts->nMinPoints =
137✔
3843
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
137✔
3844

3845
            pszValue = CSLFetchNameValue(papszParams, "nodata");
137✔
3846
            poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
137✔
3847

3848
            static const char *const apszKnownOptions[] = {
3849
                "power",      "smoothing",  "radius1", "radius2", "angle",
3850
                "max_points", "min_points", "nodata",  nullptr};
3851
            papszKnownOptions = apszKnownOptions;
137✔
3852

3853
            break;
137✔
3854
        }
3855
        case GGA_InverseDistanceToAPowerNearestNeighbor:
11✔
3856
        {
3857
            *ppOptions = CPLMalloc(
11✔
3858
                sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
3859

3860
            GDALGridInverseDistanceToAPowerNearestNeighborOptions
3861
                *const poPowerOpts = static_cast<
11✔
3862
                    GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3863
                    *ppOptions);
3864

3865
            poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
11✔
3866

3867
            const char *pszValue = CSLFetchNameValue(papszParams, "power");
11✔
3868
            poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
11✔
3869

3870
            pszValue = CSLFetchNameValue(papszParams, "smoothing");
11✔
3871
            poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
11✔
3872

3873
            pszValue = CSLFetchNameValue(papszParams, "radius");
11✔
3874
            poPowerOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : 1.0;
11✔
3875
            if (!(poPowerOpts->dfRadius > 0))
11✔
3876
            {
3877
                CPLError(CE_Failure, CPLE_IllegalArg,
×
3878
                         "Radius value should be strictly positive");
3879
                CSLDestroy(papszParams);
×
3880
                return CE_Failure;
×
3881
            }
3882

3883
            pszValue = CSLFetchNameValue(papszParams, "max_points");
11✔
3884
            poPowerOpts->nMaxPoints =
11✔
3885
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 12);
11✔
3886

3887
            pszValue = CSLFetchNameValue(papszParams, "min_points");
11✔
3888
            poPowerOpts->nMinPoints =
11✔
3889
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
11✔
3890

3891
            pszValue = CSLFetchNameValue(papszParams, "nodata");
11✔
3892
            poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
11✔
3893

3894
            pszValue =
3895
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
11✔
3896
            poPowerOpts->nMinPointsPerQuadrant =
11✔
3897
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
11✔
3898

3899
            pszValue =
3900
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
11✔
3901
            poPowerOpts->nMaxPointsPerQuadrant =
11✔
3902
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
11✔
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
            papszKnownOptions = apszKnownOptions;
11✔
3915

3916
            break;
11✔
3917
        }
3918
        case GGA_MovingAverage:
13✔
3919
        {
3920
            *ppOptions = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
13✔
3921

3922
            GDALGridMovingAverageOptions *const poAverageOpts =
13✔
3923
                static_cast<GDALGridMovingAverageOptions *>(*ppOptions);
3924

3925
            poAverageOpts->nSizeOfStructure = sizeof(*poAverageOpts);
13✔
3926

3927
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
13✔
3928
            if (pszValue)
13✔
3929
            {
3930
                poAverageOpts->dfRadius1 = CPLAtofM(pszValue);
6✔
3931
                poAverageOpts->dfRadius2 = poAverageOpts->dfRadius1;
6✔
3932
            }
3933
            else
3934
            {
3935
                pszValue = CSLFetchNameValue(papszParams, "radius1");
7✔
3936
                poAverageOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
7✔
3937

3938
                pszValue = CSLFetchNameValue(papszParams, "radius2");
7✔
3939
                poAverageOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
7✔
3940
            }
3941

3942
            pszValue = CSLFetchNameValue(papszParams, "angle");
13✔
3943
            poAverageOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
13✔
3944

3945
            pszValue = CSLFetchNameValue(papszParams, "min_points");
13✔
3946
            poAverageOpts->nMinPoints =
13✔
3947
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
13✔
3948

3949
            pszValue = CSLFetchNameValue(papszParams, "max_points");
13✔
3950
            poAverageOpts->nMaxPoints =
13✔
3951
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
13✔
3952

3953
            pszValue = CSLFetchNameValue(papszParams, "nodata");
13✔
3954
            poAverageOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
13✔
3955

3956
            pszValue =
3957
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
13✔
3958
            poAverageOpts->nMinPointsPerQuadrant =
13✔
3959
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
13✔
3960

3961
            pszValue =
3962
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
13✔
3963
            poAverageOpts->nMaxPointsPerQuadrant =
13✔
3964
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
13✔
3965

3966
            if (poAverageOpts->nMinPointsPerQuadrant != 0 ||
13✔
3967
                poAverageOpts->nMaxPointsPerQuadrant != 0)
7✔
3968
            {
3969
                if (!(poAverageOpts->dfRadius1 > 0) ||
6✔
3970
                    !(poAverageOpts->dfRadius2 > 0))
6✔
3971
                {
3972
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
3973
                             "Radius value should be strictly positive when "
3974
                             "per quadrant parameters are specified");
3975
                    CSLDestroy(papszParams);
×
3976
                    return CE_Failure;
×
3977
                }
3978
                if (poAverageOpts->dfAngle != 0)
6✔
3979
                {
3980
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3981
                             "angle != 0 not supported when "
3982
                             "per quadrant parameters are specified");
3983
                    CSLDestroy(papszParams);
×
3984
                    return CE_Failure;
×
3985
                }
3986
            }
3987
            else if (poAverageOpts->nMaxPoints > 0)
7✔
3988
            {
3989
                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
            papszKnownOptions = apszKnownOptions;
13✔
4007

4008
            break;
13✔
4009
        }
4010
        case GGA_NearestNeighbor:
14✔
4011
        {
4012
            *ppOptions = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
14✔
4013

4014
            GDALGridNearestNeighborOptions *const poNeighborOpts =
14✔
4015
                static_cast<GDALGridNearestNeighborOptions *>(*ppOptions);
4016

4017
            poNeighborOpts->nSizeOfStructure = sizeof(*poNeighborOpts);
14✔
4018

4019
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
14✔
4020
            if (pszValue)
14✔
4021
            {
4022
                poNeighborOpts->dfRadius1 = CPLAtofM(pszValue);
×
4023
                poNeighborOpts->dfRadius2 = poNeighborOpts->dfRadius1;
×
4024
            }
4025
            else
4026
            {
4027
                pszValue = CSLFetchNameValue(papszParams, "radius1");
14✔
4028
                poNeighborOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
14✔
4029

4030
                pszValue = CSLFetchNameValue(papszParams, "radius2");
14✔
4031
                poNeighborOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
14✔
4032
            }
4033

4034
            pszValue = CSLFetchNameValue(papszParams, "angle");
14✔
4035
            poNeighborOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
14✔
4036

4037
            pszValue = CSLFetchNameValue(papszParams, "nodata");
14✔
4038
            poNeighborOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
14✔
4039

4040
            static const char *const apszKnownOptions[] = {
4041
                "radius", "radius1", "radius2", "angle", "nodata", nullptr};
4042
            papszKnownOptions = apszKnownOptions;
14✔
4043

4044
            break;
14✔
4045
        }
4046
        case GGA_MetricMinimum:
51✔
4047
        case GGA_MetricMaximum:
4048
        case GGA_MetricRange:
4049
        case GGA_MetricCount:
4050
        case GGA_MetricAverageDistance:
4051
        case GGA_MetricAverageDistancePts:
4052
        {
4053
            *ppOptions = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
51✔
4054

4055
            GDALGridDataMetricsOptions *const poMetricsOptions =
51✔
4056
                static_cast<GDALGridDataMetricsOptions *>(*ppOptions);
4057

4058
            poMetricsOptions->nSizeOfStructure = sizeof(*poMetricsOptions);
51✔
4059

4060
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
51✔
4061
            if (pszValue)
51✔
4062
            {
4063
                poMetricsOptions->dfRadius1 = CPLAtofM(pszValue);
25✔
4064
                poMetricsOptions->dfRadius2 = poMetricsOptions->dfRadius1;
25✔
4065
            }
4066
            else
4067
            {
4068
                pszValue = CSLFetchNameValue(papszParams, "radius1");
26✔
4069
                poMetricsOptions->dfRadius1 =
26✔
4070
                    pszValue ? CPLAtofM(pszValue) : 0.0;
26✔
4071

4072
                pszValue = CSLFetchNameValue(papszParams, "radius2");
26✔
4073
                poMetricsOptions->dfRadius2 =
26✔
4074
                    pszValue ? CPLAtofM(pszValue) : 0.0;
26✔
4075
            }
4076

4077
            pszValue = CSLFetchNameValue(papszParams, "angle");
51✔
4078
            poMetricsOptions->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
51✔
4079

4080
            pszValue = CSLFetchNameValue(papszParams, "min_points");
51✔
4081
            poMetricsOptions->nMinPoints = pszValue ? atoi(pszValue) : 0;
51✔
4082

4083
            pszValue = CSLFetchNameValue(papszParams, "nodata");
51✔
4084
            poMetricsOptions->dfNoDataValue =
51✔
4085
                pszValue ? CPLAtofM(pszValue) : 0.0;
51✔
4086

4087
            pszValue =
4088
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
51✔
4089
            poMetricsOptions->nMinPointsPerQuadrant =
51✔
4090
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
51✔
4091

4092
            pszValue =
4093
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
51✔
4094
            poMetricsOptions->nMaxPointsPerQuadrant =
51✔
4095
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
51✔
4096

4097
            if (poMetricsOptions->nMinPointsPerQuadrant != 0 ||
51✔
4098
                poMetricsOptions->nMaxPointsPerQuadrant != 0)
26✔
4099
            {
4100
                if (*peAlgorithm == GGA_MetricAverageDistancePts)
25✔
4101
                {
4102
                    CPLError(CE_Failure, CPLE_NotSupported,
×
4103
                             "Algorithm %s not supported when "
4104
                             "per quadrant parameters are specified",
4105
                             szAlgNameAverageDistancePts);
4106
                    CSLDestroy(papszParams);
×
4107
                    return CE_Failure;
×
4108
                }
4109
                if (!(poMetricsOptions->dfRadius1 > 0) ||
25✔
4110
                    !(poMetricsOptions->dfRadius2 > 0))
25✔
4111
                {
4112
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
4113
                             "Radius value should be strictly positive when "
4114
                             "per quadrant parameters are specified");
4115
                    CSLDestroy(papszParams);
×
4116
                    return CE_Failure;
×
4117
                }
4118
                if (poMetricsOptions->dfAngle != 0)
25✔
4119
                {
4120
                    CPLError(CE_Failure, CPLE_NotSupported,
×
4121
                             "angle != 0 not supported when "
4122
                             "per quadrant parameters are specified");
4123
                    CSLDestroy(papszParams);
×
4124
                    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
            papszKnownOptions = apszKnownOptions;
51✔
4139

4140
            break;
51✔
4141
        }
4142
        case GGA_Linear:
2✔
4143
        {
4144
            *ppOptions = CPLMalloc(sizeof(GDALGridLinearOptions));
2✔
4145

4146
            GDALGridLinearOptions *const poLinearOpts =
2✔
4147
                static_cast<GDALGridLinearOptions *>(*ppOptions);
4148

4149
            poLinearOpts->nSizeOfStructure = sizeof(*poLinearOpts);
2✔
4150

4151
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
2✔
4152
            poLinearOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : -1.0;
2✔
4153

4154
            pszValue = CSLFetchNameValue(papszParams, "nodata");
2✔
4155
            poLinearOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
2✔
4156

4157
            static const char *const apszKnownOptions[] = {"radius", "nodata",
4158
                                                           nullptr};
4159
            papszKnownOptions = apszKnownOptions;
2✔
4160

4161
            break;
2✔
4162
        }
4163
    }
4164

4165
    if (papszKnownOptions)
228✔
4166
    {
4167
        for (int i = 1; papszParams[i] != nullptr; ++i)
624✔
4168
        {
4169
            char *pszKey = nullptr;
396✔
4170
            CPL_IGNORE_RET_VAL(CPLParseNameValue(papszParams[i], &pszKey));
396✔
4171
            if (pszKey)
396✔
4172
            {
4173
                bool bKnownKey = false;
396✔
4174
                for (const char *const *papszKnownKeyIter = papszKnownOptions;
1,677✔
4175
                     *papszKnownKeyIter; ++papszKnownKeyIter)
1,677✔
4176
                {
4177
                    if (EQUAL(*papszKnownKeyIter, pszKey))
1,677✔
4178
                    {
4179
                        bKnownKey = true;
396✔
4180
                        break;
396✔
4181
                    }
4182
                }
4183
                if (!bKnownKey)
396✔
4184
                {
4185
                    CPLError(CE_Warning, CPLE_AppDefined, "Option %s ignored",
×
4186
                             pszKey);
4187
                }
4188
            }
4189
            CPLFree(pszKey);
396✔
4190
        }
4191
    }
4192

4193
    CSLDestroy(papszParams);
228✔
4194
    return CE_None;
228✔
4195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc