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

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

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

91.78
/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)
5,857,720✔
47
{
48
    const GDALGridPoint *psPoint = static_cast<const GDALGridPoint *>(hFeature);
5,857,720✔
49
    GDALGridXYArrays *psXYArrays = psPoint->psXYArrays;
5,857,720✔
50
    const int i = psPoint->i;
5,857,720✔
51
    const double dfX = psXYArrays->padfX[i];
5,857,720✔
52
    const double dfY = psXYArrays->padfY[i];
5,857,720✔
53
    pBounds->minx = dfX;
5,857,720✔
54
    pBounds->miny = dfY;
5,857,720✔
55
    pBounds->maxx = dfX;
5,857,720✔
56
    pBounds->maxy = dfY;
5,857,720✔
57
}
5,857,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,
448,291✔
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 =
448,291✔
119
        static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
120
            poOptionsIn);
121

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

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

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

140
    for (GUInt32 i = 0; i < nPoints; i++)
2,176,500✔
141
    {
142
        double dfRX = padfX[i] - dfXPoint;
1,782,980✔
143
        double dfRY = padfY[i] - dfYPoint;
1,782,980✔
144
        const double dfR2 =
1,782,980✔
145
            dfRX * dfRX + dfRY * dfRY + dfSmoothing * dfSmoothing;
1,782,980✔
146

147
        if (bRotated)
1,782,980✔
148
        {
149
            const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
225,858✔
150
            const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
225,858✔
151

152
            dfRX = dfRXRotated;
225,858✔
153
            dfRY = dfRYRotated;
225,858✔
154
        }
155

156
        // Is this point located inside the search ellipse?
157
        if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
1,782,980✔
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)
1,238,050✔
163
            {
164
                *pdfValue = padfZ[i];
×
165
                return CE_None;
×
166
            }
167

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

178
    if (n < poOptions->nMinPoints || dfDenominator == 0.0)
448,291✔
179
    {
UNCOV
180
        *pdfValue = poOptions->dfNoDataValue;
×
181
    }
182
    else
183
    {
184
        *pdfValue = dfNominator / dfDenominator;
460,791✔
185
    }
186

187
    return CE_None;
448,291✔
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(
450,370✔
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);
450,370✔
247

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

256
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
448,043✔
257

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

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

266
    std::multimap<double, double> oMapDistanceToZValues;
896,541✔
267

268
    const double dfSearchRadius = dfRadius;
444,153✔
269
    CPLRectObj sAoi;
270
    sAoi.minx = dfXPoint - dfSearchRadius;
444,153✔
271
    sAoi.miny = dfYPoint - dfSearchRadius;
444,153✔
272
    sAoi.maxx = dfXPoint + dfSearchRadius;
444,153✔
273
    sAoi.maxy = dfYPoint + dfSearchRadius;
444,153✔
274
    int nFeatureCount = 0;
444,153✔
275
    GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
276
        CPLQuadTreeSearch(phQuadTree, &sAoi, &nFeatureCount));
444,153✔
277
    if (nFeatureCount != 0)
445,710✔
278
    {
279
        for (int k = 0; k < nFeatureCount; k++)
2,657,950✔
280
        {
281
            const int i = papsPoints[k]->i;
2,244,600✔
282
            const double dfRX = padfX[i] - dfXPoint;
2,244,600✔
283
            const double dfRY = padfY[i] - dfYPoint;
2,244,600✔
284

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

304
    double dfNominator = 0.0;
450,154✔
305
    double dfDenominator = 0.0;
450,154✔
306
    GUInt32 n = 0;
450,154✔
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 =
1,820,980✔
312
             oMapDistanceToZValues.begin();
450,154✔
313
         oMapDistanceToZValuesIter != oMapDistanceToZValues.end();
2,260,920✔
314
         ++oMapDistanceToZValuesIter)
1,788,430✔
315
    {
316
        const double dfR2 = oMapDistanceToZValuesIter->first;
1,848,220✔
317
        const double dfZ = oMapDistanceToZValuesIter->second;
1,827,620✔
318

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

330
    if (n < poOptions->nMinPoints || dfDenominator == 0.0)
448,498✔
331
    {
332
        *pdfValue = poOptions->dfNoDataValue;
64,024✔
333
    }
334
    else
335
    {
336
        *pdfValue = dfNominator / dfDenominator;
384,474✔
337
    }
338

339
    return CE_None;
448,498✔
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(
258,212✔
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<
258,212✔
357
            const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
358
            poOptionsIn);
359
    const double dfRadius = poOptions->dfRadius;
258,212✔
360
    const double dfSmoothing = poOptions->dfSmoothing;
258,212✔
361
    const double dfSmoothing2 = dfSmoothing * dfSmoothing;
258,212✔
362

363
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
258,212✔
364
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
258,212✔
365
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
258,212✔
366

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

372
    const double dfRPower2 = psExtraParams->dfRadiusPower2PreComp;
258,212✔
373
    const double dfPowerDiv2 = psExtraParams->dfPowerDiv2PreComp;
258,212✔
374
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
2,518,270✔
375

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

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

414
    std::multimap<double, double>::iterator aoIter[] = {
415
        oMapDistanceToZValuesPerQuadrant[0].begin(),
256,975✔
416
        oMapDistanceToZValuesPerQuadrant[1].begin(),
251,335✔
417
        oMapDistanceToZValuesPerQuadrant[2].begin(),
253,123✔
418
        oMapDistanceToZValuesPerQuadrant[3].begin(),
253,744✔
419
    };
256,975✔
420
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
251,379✔
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 again with the next nearest
427
    // point in quarant 0, etc.
428
    int nQuadrantIterFinishedFlag = 0;
251,379✔
429
    GUInt32 anPerQuadrant[4] = {0};
251,379✔
430
    double dfNominator = 0.0;
251,379✔
431
    double dfDenominator = 0.0;
251,379✔
432
    GUInt32 n = 0;
251,379✔
433
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
251,379✔
434
    {
435
        if (aoIter[iQuadrant] ==
2,275,000✔
436
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
5,227,900✔
437
            (nMaxPointsPerQuadrant > 0 &&
561,969✔
438
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
561,969✔
439
        {
440
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
1,320,560✔
441
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
1,320,560✔
442
                break;
258,117✔
443
            continue;
1,062,440✔
444
        }
445

446
        const double dfR2 = aoIter[iQuadrant]->first;
1,026,550✔
447
        const double dfZ = aoIter[iQuadrant]->second;
1,008,600✔
448
        ++aoIter[iQuadrant];
1,015,300✔
449

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

462
    if (nMinPointsPerQuadrant > 0 &&
258,118✔
463
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
129,163✔
464
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
52,240✔
465
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
42,439✔
466
         anPerQuadrant[3] < nMinPointsPerQuadrant))
33,234✔
467
    {
468
        *pdfValue = poOptions->dfNoDataValue;
100,077✔
469
    }
470
    else if (n < poOptions->nMinPoints || dfDenominator == 0.0)
158,041✔
471
    {
472
        *pdfValue = poOptions->dfNoDataValue;
×
473
    }
474
    else
475
    {
476
        *pdfValue = dfNominator / dfDenominator;
158,735✔
477
    }
478

479
    return CE_None;
258,118✔
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(
118,080✔
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 =
118,080✔
503
        static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
504
            poOptionsIn);
505
    const double dfPowerDiv2 = poOptions->dfPower / 2.0;
118,080✔
506
    const double dfSmoothing = poOptions->dfSmoothing;
118,080✔
507
    const double dfSmoothing2 = dfSmoothing * dfSmoothing;
118,080✔
508
    double dfNominator = 0.0;
118,080✔
509
    double dfDenominator = 0.0;
118,080✔
510
    const bool bPower2 = dfPowerDiv2 == 1.0;
118,080✔
511

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

523
                const double dfInvR2 = 1.0 / dfR2;
173,773✔
524
                dfNominator += dfInvR2 * padfZ[i];
173,773✔
525
                dfDenominator += dfInvR2;
173,773✔
526
            }
527
        }
528
        else
529
        {
530
            for (i = 0; i < nPoints; i++)
80,419✔
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++)
307,123✔
552
        {
553
            const double dfRX = padfX[i] - dfXPoint;
251,317✔
554
            const double dfRY = padfY[i] - dfYPoint;
251,317✔
555
            const double dfR2 = dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
251,317✔
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)
251,317✔
560
            {
561
                break;
×
562
            }
563

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

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

584
    return CE_None;
118,080✔
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,
532,989✔
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 =
532,989✔
639
        static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
640
    // Pre-compute search ellipse parameters.
641
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
532,989✔
642
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
532,989✔
643
    const double dfSearchRadius =
644
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
532,989✔
645
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
584,209✔
646

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

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

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

658
    double dfAccumulator = 0.0;
584,209✔
659

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

679
                if (dfRadius2Square * dfRX * dfRX +
38,944✔
680
                        dfRadius1Square * dfRY * dfRY <=
38,944✔
681
                    dfR12Square)
682
                {
683
                    dfAccumulator += padfZ[i];
31,865✔
684
                    n++;
31,865✔
685
                }
686
            }
687
        }
688
        CPLFree(papsPoints);
1,596✔
689
    }
690
    else
691
    {
692
        for (GUInt32 i = 0; i < nPoints; i++)
3,147,860✔
693
        {
694
            double dfRX = padfX[i] - dfXPoint;
2,565,250✔
695
            double dfRY = padfY[i] - dfYPoint;
2,565,250✔
696

697
            if (bRotated)
2,565,250✔
698
            {
699
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
342,708✔
700
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
342,708✔
701

702
                dfRX = dfRXRotated;
342,708✔
703
                dfRY = dfRYRotated;
342,708✔
704
            }
705

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

716
    if (n < poOptions->nMinPoints || n == 0)
584,212✔
717
    {
718
        *pdfValue = poOptions->dfNoDataValue;
81,396✔
719
    }
720
    else
721
    {
722
        *pdfValue = dfAccumulator / n;
502,816✔
723
    }
724

725
    return CE_None;
584,212✔
726
}
727

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

732
/**
733
 * Moving average, with a per-quadrant search logic.
734
 */
735
static CPLErr GDALGridMovingAveragePerQuadrant(
191,487✔
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 =
191,487✔
741
        static_cast<const GDALGridMovingAverageOptions *>(poOptionsIn);
742
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
191,487✔
743
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
191,487✔
744
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
191,487✔
745

746
    const GUInt32 nMaxPoints = poOptions->nMaxPoints;
191,487✔
747
    const GUInt32 nMinPointsPerQuadrant = poOptions->nMinPointsPerQuadrant;
191,487✔
748
    const GUInt32 nMaxPointsPerQuadrant = poOptions->nMaxPointsPerQuadrant;
191,487✔
749

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

755
    std::multimap<double, double> oMapDistanceToZValuesPerQuadrant[4];
1,868,880✔
756

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

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

789
    std::multimap<double, double>::iterator aoIter[] = {
790
        oMapDistanceToZValuesPerQuadrant[0].begin(),
191,649✔
791
        oMapDistanceToZValuesPerQuadrant[1].begin(),
183,990✔
792
        oMapDistanceToZValuesPerQuadrant[2].begin(),
185,306✔
793
        oMapDistanceToZValuesPerQuadrant[3].begin(),
186,348✔
794
    };
191,649✔
795
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
186,522✔
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 again with the next nearest
802
    // point in quarant 0, etc.
803
    int nQuadrantIterFinishedFlag = 0;
186,522✔
804
    GUInt32 anPerQuadrant[4] = {0};
186,522✔
805
    double dfNominator = 0.0;
186,522✔
806
    GUInt32 n = 0;
186,522✔
807
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
186,522✔
808
    {
809
        if (aoIter[iQuadrant] ==
900,942✔
810
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
2,116,200✔
811
            (nMaxPointsPerQuadrant > 0 &&
305,698✔
812
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
305,698✔
813
        {
814
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
244,649✔
815
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
244,649✔
816
                break;
64,266✔
817
            continue;
180,383✔
818
        }
819

820
        const double dfZ = aoIter[iQuadrant]->second;
664,559✔
821
        ++aoIter[iQuadrant];
645,035✔
822

823
        dfNominator += dfZ;
659,044✔
824
        n++;
659,044✔
825
        anPerQuadrant[iQuadrant]++;
659,044✔
826
        if (nMaxPoints > 0 && n >= nMaxPoints)
659,044✔
827
        {
828
            break;
124,655✔
829
        }
830
    }
714,772✔
831

832
    if (nMinPointsPerQuadrant > 0 &&
188,921✔
833
        (anPerQuadrant[0] < nMinPointsPerQuadrant ||
125,409✔
834
         anPerQuadrant[1] < nMinPointsPerQuadrant ||
125,392✔
835
         anPerQuadrant[2] < nMinPointsPerQuadrant ||
125,282✔
836
         anPerQuadrant[3] < nMinPointsPerQuadrant))
125,015✔
837
    {
838
        *pdfValue = poOptions->dfNoDataValue;
62,591✔
839
    }
840
    else if (n < poOptions->nMinPoints || n == 0)
126,330✔
841
    {
842
        *pdfValue = poOptions->dfNoDataValue;
×
843
    }
844
    else
845
    {
846
        *pdfValue = dfNominator / n;
127,186✔
847
    }
848

849
    return CE_None;
380,227✔
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,
484,933✔
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 =
484,933✔
889
        static_cast<const GDALGridNearestNeighborOptions *>(poOptionsIn);
890
    // Pre-compute search ellipse parameters.
891
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
484,933✔
892
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
484,933✔
893
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
484,933✔
894
    GDALGridExtraParameters *psExtraParams =
484,933✔
895
        static_cast<GDALGridExtraParameters *>(hExtraParamsIn);
896
    CPLQuadTree *hQuadTree = psExtraParams->hQuadTree;
484,933✔
897

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

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

908
    double dfSearchRadius = psExtraParams->dfInitialSearchRadius;
484,933✔
909
    if (hQuadTree != nullptr)
484,933✔
910
    {
911
        if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
137,375✔
912
            dfSearchRadius =
64,985✔
913
                std::max(poOptions->dfRadius1, poOptions->dfRadius2);
65,510✔
914
        CPLRectObj sAoi;
915
        while (dfSearchRadius > 0)
318,490✔
916
        {
917
            sAoi.minx = dfXPoint - dfSearchRadius;
290,950✔
918
            sAoi.miny = dfYPoint - dfSearchRadius;
290,950✔
919
            sAoi.maxx = dfXPoint + dfSearchRadius;
290,950✔
920
            sAoi.maxy = dfYPoint + dfSearchRadius;
290,950✔
921
            int nFeatureCount = 0;
290,950✔
922
            GDALGridPoint **papsPoints = reinterpret_cast<GDALGridPoint **>(
923
                CPLQuadTreeSearch(hQuadTree, &sAoi, &nFeatureCount));
290,950✔
924
            if (nFeatureCount != 0)
330,095✔
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();
76,206✔
929
                for (int k = 0; k < nFeatureCount; k++)
360,049✔
930
                {
931
                    const int idx = papsPoints[k]->i;
283,843✔
932
                    const double dfRX = padfX[idx] - dfXPoint;
283,843✔
933
                    const double dfRY = padfY[idx] - dfYPoint;
283,843✔
934

935
                    const double dfR2 = dfRX * dfRX + dfRY * dfRY;
283,843✔
936
                    if (dfR2 <= dfNearestRSquare)
283,843✔
937
                    {
938
                        dfNearestRSquare = dfR2;
170,814✔
939
                        dfNearestValue = padfZ[idx];
170,814✔
940
                    }
941
                }
942

943
                CPLFree(papsPoints);
76,206✔
944
                break;
151,228✔
945
            }
946

947
            CPLFree(papsPoints);
253,889✔
948
            if (poOptions->dfRadius1 > 0 || poOptions->dfRadius2 > 0)
256,934✔
949
                break;
950
            dfSearchRadius *= 2;
181,640✔
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();
347,558✔
960
        while (i < nPoints)
226,993,000✔
961
        {
962
            double dfRX = padfX[i] - dfXPoint;
226,645,000✔
963
            double dfRY = padfY[i] - dfYPoint;
226,645,000✔
964

965
            if (bRotated)
226,645,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;
226,645,000✔
976
            const double dfRYSquare = dfRY * dfRY;
226,645,000✔
977
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
226,645,000✔
978
                dfR12Square)
979
            {
980
                const double dfR2 = dfRXSquare + dfRYSquare;
206,447,000✔
981
                if (dfR2 <= dfNearestRSquare)
206,447,000✔
982
                {
983
                    dfNearestRSquare = dfR2;
16,479,100✔
984
                    dfNearestValue = padfZ[i];
16,479,100✔
985
                }
986
            }
987

988
            i++;
226,645,000✔
989
        }
990
    }
991

992
    *pdfValue = dfNearestValue;
526,326✔
993

994
    return CE_None;
526,326✔
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,
272,347✔
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 =
272,347✔
1043
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1044

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

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

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

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

1082
                if (dfRadius2Square * dfRX * dfRX +
32,166✔
1083
                        dfRadius1Square * dfRY * dfRY <=
32,166✔
1084
                    dfR12Square)
1085
                {
1086
                    if (dfMinimumValue > padfZ[i])
20,596✔
1087
                        dfMinimumValue = padfZ[i];
3,259✔
1088
                    n++;
20,596✔
1089
                }
1090
            }
1091
        }
1092
        CPLFree(papsPoints);
1,595✔
1093
    }
1094
    else
1095
    {
1096
        GUInt32 i = 0;
276,429✔
1097
        while (i < nPoints)
1,449,760✔
1098
        {
1099
            double dfRX = padfX[i] - dfXPoint;
1,173,340✔
1100
            double dfRY = padfY[i] - dfYPoint;
1,173,340✔
1101

1102
            if (bRotated)
1,173,340✔
1103
            {
1104
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
65,124✔
1105
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
65,124✔
1106

1107
                dfRX = dfRXRotated;
65,124✔
1108
                dfRY = dfRYRotated;
65,124✔
1109
            }
1110

1111
            // Is this point located inside the search ellipse?
1112
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
1,173,340✔
1113
                dfR12Square)
1114
            {
1115
                if (dfMinimumValue > padfZ[i])
496,727✔
1116
                    dfMinimumValue = padfZ[i];
335,543✔
1117
                n++;
496,727✔
1118
            }
1119

1120
            i++;
1,173,340✔
1121
        }
1122
    }
1123

1124
    if (n < poOptions->nMinPoints || n == 0)
278,026✔
1125
    {
1126
        *pdfValue = poOptions->dfNoDataValue;
61,869✔
1127
    }
1128
    else
1129
    {
1130
        *pdfValue = dfMinimumValue;
216,157✔
1131
    }
1132

1133
    return CE_None;
278,026✔
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(
127,909✔
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 =
127,909✔
1151
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1152

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

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

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

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

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

1189
            if (dfRadius2Square * dfRXSquare + dfRadius1Square * dfRYSquare <=
396,363✔
1190
                dfR12Square)
1191
            {
1192
                const int iQuadrant =
385,273✔
1193
                    ((dfRX >= 0) ? 1 : 0) | (((dfRY >= 0) ? 1 : 0) << 1);
385,273✔
1194
                oMapDistanceToZValuesPerQuadrant[iQuadrant].insert(
371,762✔
1195
                    std::make_pair(dfRXSquare + dfRYSquare, padfZ[i]));
765,073✔
1196
            }
1197
        }
1198
    }
1199
    CPLFree(papsPoints);
122,520✔
1200

1201
    std::multimap<double, double>::iterator aoIter[] = {
631,652✔
1202
        oMapDistanceToZValuesPerQuadrant[0].begin(),
128,933✔
1203
        oMapDistanceToZValuesPerQuadrant[1].begin(),
125,427✔
1204
        oMapDistanceToZValuesPerQuadrant[2].begin(),
126,273✔
1205
        oMapDistanceToZValuesPerQuadrant[3].begin(),
126,611✔
1206
    };
1207
    constexpr int ALL_QUADRANT_FLAGS = 1 + 2 + 4 + 8;
124,408✔
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 again with the next nearest
1214
    // point in quarant 0, etc.
1215
    int nQuadrantIterFinishedFlag = 0;
124,408✔
1216
    GUInt32 anPerQuadrant[4] = {0};
124,408✔
1217
    double dfExtremum = IS_MIN ? std::numeric_limits<double>::max()
124,408✔
1218
                               : -std::numeric_limits<double>::max();
1219
    GUInt32 n = 0;
124,408✔
1220
    for (int iQuadrant = 0; /* true */; iQuadrant = (iQuadrant + 1) % 4)
888,934✔
1221
    {
1222
        if (aoIter[iQuadrant] ==
876,381✔
1223
                oMapDistanceToZValuesPerQuadrant[iQuadrant].end() ||
2,097,140✔
1224
            (nMaxPointsPerQuadrant > 0 &&
308,432✔
1225
             anPerQuadrant[iQuadrant] >= nMaxPointsPerQuadrant))
308,432✔
1226
        {
1227
            nQuadrantIterFinishedFlag |= 1 << iQuadrant;
580,147✔
1228
            if (nQuadrantIterFinishedFlag == ALL_QUADRANT_FLAGS)
580,147✔
1229
                break;
129,038✔
1230
            continue;
451,109✔
1231
        }
1232

1233
        const double dfZ = aoIter[iQuadrant]->second;
319,623✔
1234
        ++aoIter[iQuadrant];
308,941✔
1235

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

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

1271
    return CE_None;
257,436✔
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(
125,806✔
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>(
125,806✔
1287
        poOptionsIn, padfX, padfY, padfZ, dfXPoint, dfYPoint, pdfValue,
1288
        hExtraParamsIn);
129,339✔
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,
58,830✔
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 =
58,830✔
1337
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1338

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

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

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

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

1376
                if (dfRadius2Square * dfRX * dfRX +
34,656✔
1377
                        dfRadius1Square * dfRY * dfRY <=
34,656✔
1378
                    dfR12Square)
1379
                {
1380
                    if (dfMaximumValue < padfZ[i])
23,073✔
1381
                        dfMaximumValue = padfZ[i];
3,325✔
1382
                    n++;
23,073✔
1383
                }
1384
            }
1385
        }
1386
        CPLFree(papsPoints);
1,197✔
1387
    }
1388
    else
1389
    {
1390
        GUInt32 i = 0;
58,189✔
1391
        while (i < nPoints)
442,113✔
1392
        {
1393
            double dfRX = padfX[i] - dfXPoint;
383,924✔
1394
            double dfRY = padfY[i] - dfYPoint;
383,924✔
1395

1396
            if (bRotated)
383,924✔
1397
            {
1398
                const double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
114,049✔
1399
                const double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
114,049✔
1400

1401
                dfRX = dfRXRotated;
114,049✔
1402
                dfRY = dfRYRotated;
114,049✔
1403
            }
1404

1405
            // Is this point located inside the search ellipse?
1406
            if (dfRadius2Square * dfRX * dfRX + dfRadius1Square * dfRY * dfRY <=
383,924✔
1407
                dfR12Square)
1408
            {
1409
                if (dfMaximumValue < padfZ[i])
262,577✔
1410
                    dfMaximumValue = padfZ[i];
140,245✔
1411
                n++;
262,577✔
1412
            }
1413

1414
            i++;
383,924✔
1415
        }
1416
    }
1417

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

1427
    return CE_None;
59,388✔
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,
58,961✔
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 =
58,961✔
1494
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1495
    // Pre-compute search ellipse parameters.
1496
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
58,961✔
1497
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
58,961✔
1498
    const double dfSearchRadius =
1499
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
58,961✔
1500
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
59,415✔
1501

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

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

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

1533
                if (dfRadius2Square * dfRX * dfRX +
6,596✔
1534
                        dfRadius1Square * dfRY * dfRY <=
6,596✔
1535
                    dfR12Square)
1536
                {
1537
                    if (dfMinimumValue > padfZ[i])
6,580✔
1538
                        dfMinimumValue = padfZ[i];
1,934✔
1539
                    if (dfMaximumValue < padfZ[i])
6,580✔
1540
                        dfMaximumValue = padfZ[i];
1,821✔
1541
                    n++;
6,580✔
1542
                }
1543
            }
1544
        }
1545
        CPLFree(papsPoints);
795✔
1546
    }
1547
    else
1548
    {
1549
        GUInt32 i = 0;
58,624✔
1550
        while (i < nPoints)
326,273✔
1551
        {
1552
            double dfRX = padfX[i] - dfXPoint;
267,649✔
1553
            double dfRY = padfY[i] - dfYPoint;
267,649✔
1554

1555
            if (bRotated)
267,649✔
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 <=
267,649✔
1566
                dfR12Square)
1567
            {
1568
                if (dfMinimumValue > padfZ[i])
226,194✔
1569
                    dfMinimumValue = padfZ[i];
149,273✔
1570
                if (dfMaximumValue < padfZ[i])
226,194✔
1571
                    dfMaximumValue = padfZ[i];
145,246✔
1572
                n++;
226,194✔
1573
            }
1574

1575
            i++;
267,649✔
1576
        }
1577
    }
1578

1579
    if (n < poOptions->nMinPoints || n == 0)
59,424✔
1580
    {
1581
        *pdfValue = poOptions->dfNoDataValue;
556✔
1582
    }
1583
    else
1584
    {
1585
        *pdfValue = dfMaximumValue - dfMinimumValue;
58,868✔
1586
    }
1587

1588
    return CE_None;
59,424✔
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 again 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,
56,962✔
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 =
56,962✔
1765
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
1766

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

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

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

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

1803
                if (dfRadius2Square * dfRX * dfRX +
77,533✔
1804
                        dfRadius1Square * dfRY * dfRY <=
77,533✔
1805
                    dfR12Square)
1806
                {
1807
                    n++;
54,681✔
1808
                }
1809
            }
1810
        }
1811
        CPLFree(papsPoints);
1,997✔
1812
    }
1813
    else
1814
    {
1815
        GUInt32 i = 0;
53,951✔
1816
        while (i < nPoints)
272,551✔
1817
        {
1818
            double dfRX = padfX[i] - dfXPoint;
218,600✔
1819
            double dfRY = padfY[i] - dfYPoint;
218,600✔
1820

1821
            if (bRotated)
218,600✔
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 <=
218,600✔
1832
                dfR12Square)
1833
            {
1834
                n++;
58,807✔
1835
            }
1836

1837
            i++;
218,600✔
1838
        }
1839
    }
1840

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

1850
    return CE_None;
55,950✔
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 again 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,
58,272✔
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 =
58,272✔
2025
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
2026

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

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

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

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

2064
                if (dfRadius2Square * dfRX * dfRX +
16,922✔
2065
                        dfRadius1Square * dfRY * dfRY <=
16,922✔
2066
                    dfR12Square)
2067
                {
2068
                    dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
14,502✔
2069
                    n++;
14,502✔
2070
                }
2071
            }
2072
        }
2073
        CPLFree(papsPoints);
795✔
2074
    }
2075
    else
2076
    {
2077
        GUInt32 i = 0;
58,460✔
2078

2079
        while (i < nPoints)
381,599✔
2080
        {
2081
            double dfRX = padfX[i] - dfXPoint;
323,139✔
2082
            double dfRY = padfY[i] - dfYPoint;
323,139✔
2083

2084
            if (bRotated)
323,139✔
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 <=
323,139✔
2095
                dfR12Square)
2096
            {
2097
                dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
301,235✔
2098
                n++;
301,235✔
2099
            }
2100

2101
            i++;
323,139✔
2102
        }
2103
    }
2104

2105
    if (n < poOptions->nMinPoints || n == 0)
59,257✔
2106
    {
2107
        *pdfValue = poOptions->dfNoDataValue;
3,323✔
2108
    }
2109
    else
2110
    {
2111
        *pdfValue = dfAccumulator / n;
55,934✔
2112
    }
2113

2114
    return CE_None;
59,257✔
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 again 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(
57,910✔
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 =
57,910✔
2292
        static_cast<const GDALGridDataMetricsOptions *>(poOptionsIn);
2293
    // Pre-compute search ellipse parameters.
2294
    const double dfRadius1Square = poOptions->dfRadius1 * poOptions->dfRadius1;
57,910✔
2295
    const double dfRadius2Square = poOptions->dfRadius2 * poOptions->dfRadius2;
57,910✔
2296
    const double dfSearchRadius =
2297
        std::max(poOptions->dfRadius1, poOptions->dfRadius2);
57,910✔
2298
    const double dfR12Square = dfRadius1Square * dfRadius2Square;
63,529✔
2299

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

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

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

2330
                if (dfRadius2Square * dfRX1 * dfRX1 +
16,096✔
2331
                        dfRadius1Square * dfRY1 * dfRY1 <=
16,096✔
2332
                    dfR12Square)
2333
                {
2334
                    for (int j = k; j < nFeatureCount; j++)
133,260✔
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;
119,134✔
2339
                        double dfRX2 = padfX[ji] - dfXPoint;
119,134✔
2340
                        double dfRY2 = padfY[ji] - dfYPoint;
119,134✔
2341

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

2349
                            dfAccumulator += sqrt(dfRX * dfRX + dfRY * dfRY);
109,168✔
2350
                            n++;
109,168✔
2351
                        }
2352
                    }
2353
                }
2354
            }
2355
        }
2356
        CPLFree(papsPoints);
800✔
2357
    }
2358
    else
2359
    {
2360
        GUInt32 i = 0;
62,731✔
2361
        while (i < nPoints - 1)
372,562✔
2362
        {
2363
            double dfRX1 = padfX[i] - dfXPoint;
309,831✔
2364
            double dfRY1 = padfY[i] - dfYPoint;
309,831✔
2365

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

2371
                dfRX1 = dfRXRotated;
133,017✔
2372
                dfRY1 = dfRYRotated;
133,017✔
2373
            }
2374

2375
            // Is this point located inside the search ellipse?
2376
            if (dfRadius2Square * dfRX1 * dfRX1 +
309,831✔
2377
                    dfRadius1Square * dfRY1 * dfRY1 <=
309,831✔
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++)
795,951✔
2383
                {
2384
                    double dfRX2 = padfX[j] - dfXPoint;
594,540✔
2385
                    double dfRY2 = padfY[j] - dfYPoint;
594,540✔
2386

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

2394
                        dfRX2 = dfRXRotated;
242,475✔
2395
                        dfRY2 = dfRYRotated;
242,475✔
2396
                    }
2397

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

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

2411
            i++;
309,831✔
2412
        }
2413
    }
2414

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

2425
    return CE_None;
63,530✔
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,
310,164✔
2460
                      const double *padfX, const double *padfY,
2461
                      const double *padfZ, double dfXPoint, double dfYPoint,
2462
                      double *pdfValue, void *hExtraParams)
2463
{
2464
    GDALGridExtraParameters *psExtraParams =
310,164✔
2465
        static_cast<GDALGridExtraParameters *>(hExtraParams);
2466
    GDALTriangulation *psTriangulation = psExtraParams->psTriangulation;
310,164✔
2467

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

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

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

2504
        const GDALGridLinearOptions *const poOptions =
237,380✔
2505
            static_cast<const GDALGridLinearOptions *>(poOptionsIn);
2506
        const double dfRadius = poOptions->dfRadius;
237,380✔
2507
        if (dfRadius == 0.0)
237,380✔
2508
        {
2509
            *pdfValue = poOptions->dfNoDataValue;
104,673✔
2510
        }
2511
        else
2512
        {
2513
            GDALGridNearestNeighborOptions sNeighbourOptions;
2514
            sNeighbourOptions.nSizeOfStructure = sizeof(sNeighbourOptions);
132,707✔
2515
            sNeighbourOptions.dfRadius1 =
129,401✔
2516
                dfRadius < 0.0 || dfRadius >= std::numeric_limits<double>::max()
123,200✔
2517
                    ? 0.0
252,601✔
2518
                    : dfRadius;
2519
            sNeighbourOptions.dfRadius2 =
129,783✔
2520
                dfRadius < 0.0 || dfRadius >= std::numeric_limits<double>::max()
121,617✔
2521
                    ? 0.0
251,400✔
2522
                    : dfRadius;
2523
            sNeighbourOptions.dfAngle = 0.0;
129,783✔
2524
            sNeighbourOptions.dfNoDataValue = poOptions->dfNoDataValue;
129,783✔
2525
            return GDALGridNearestNeighbor(&sNeighbourOptions, nPoints, padfX,
129,783✔
2526
                                           padfY, padfZ, dfXPoint, dfYPoint,
2527
                                           pdfValue, hExtraParams);
136,187✔
2528
        }
2529
    }
2530

2531
    return CE_None;
178,229✔
2532
}
2533

2534
/************************************************************************/
2535
/*                             GDALGridJob                              */
2536
/************************************************************************/
2537

2538
typedef struct _GDALGridJob GDALGridJob;
2539

2540
struct _GDALGridJob
2541
{
2542
    GUInt32 nYStart;
2543

2544
    GByte *pabyData;
2545
    GUInt32 nYStep;
2546
    GUInt32 nXSize;
2547
    GUInt32 nYSize;
2548
    double dfXMin;
2549
    double dfYMin;
2550
    double dfDeltaX;
2551
    double dfDeltaY;
2552
    GUInt32 nPoints;
2553
    const double *padfX;
2554
    const double *padfY;
2555
    const double *padfZ;
2556
    const void *poOptions;
2557
    GDALGridFunction pfnGDALGridMethod;
2558
    GDALGridExtraParameters *psExtraParameters;
2559
    int (*pfnProgress)(GDALGridJob *psJob);
2560
    GDALDataType eType;
2561

2562
    int *pnCounter;
2563
    int nCounterSingleThreaded;
2564
    volatile int *pbStop;
2565
    CPLCond *hCond;
2566
    CPLMutex *hCondMutex;
2567

2568
    GDALProgressFunc pfnRealProgress;
2569
    void *pRealProgressArg;
2570
};
2571

2572
/************************************************************************/
2573
/*                   GDALGridProgressMultiThread()                      */
2574
/************************************************************************/
2575

2576
// Return TRUE if the computation must be interrupted.
2577
static int GDALGridProgressMultiThread(GDALGridJob *psJob)
20,829✔
2578
{
2579
    CPLAcquireMutex(psJob->hCondMutex, 1.0);
20,829✔
2580
    ++(*psJob->pnCounter);
20,852✔
2581
    CPLCondSignal(psJob->hCond);
20,852✔
2582
    const int bStop = *psJob->pbStop;
20,852✔
2583
    CPLReleaseMutex(psJob->hCondMutex);
20,852✔
2584

2585
    return bStop;
20,852✔
2586
}
2587

2588
/************************************************************************/
2589
/*                      GDALGridProgressMonoThread()                    */
2590
/************************************************************************/
2591

2592
// Return TRUE if the computation must be interrupted.
2593
static int GDALGridProgressMonoThread(GDALGridJob *psJob)
40✔
2594
{
2595
    const int nCounter = ++(psJob->nCounterSingleThreaded);
40✔
2596
    if (!psJob->pfnRealProgress(nCounter / static_cast<double>(psJob->nYSize),
40✔
2597
                                "", psJob->pRealProgressArg))
2598
    {
2599
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
×
2600
        *psJob->pbStop = TRUE;
×
2601
        return TRUE;
×
2602
    }
2603
    return FALSE;
40✔
2604
}
2605

2606
/************************************************************************/
2607
/*                         GDALGridJobProcess()                         */
2608
/************************************************************************/
2609

2610
static void GDALGridJobProcess(void *user_data)
726✔
2611
{
2612
    GDALGridJob *const psJob = static_cast<GDALGridJob *>(user_data);
726✔
2613
    int (*pfnProgress)(GDALGridJob * psJob) = psJob->pfnProgress;
726✔
2614
    const GUInt32 nXSize = psJob->nXSize;
726✔
2615

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

2631
    const GUInt32 nYStart = psJob->nYStart;
725✔
2632
    const GUInt32 nYStep = psJob->nYStep;
725✔
2633
    GByte *pabyData = psJob->pabyData;
725✔
2634

2635
    const GUInt32 nYSize = psJob->nYSize;
725✔
2636
    const double dfXMin = psJob->dfXMin;
725✔
2637
    const double dfYMin = psJob->dfYMin;
725✔
2638
    const double dfDeltaX = psJob->dfDeltaX;
725✔
2639
    const double dfDeltaY = psJob->dfDeltaY;
725✔
2640
    const GUInt32 nPoints = psJob->nPoints;
725✔
2641
    const double *padfX = psJob->padfX;
725✔
2642
    const double *padfY = psJob->padfY;
725✔
2643
    const double *padfZ = psJob->padfZ;
725✔
2644
    const void *poOptions = psJob->poOptions;
725✔
2645
    GDALGridFunction pfnGDALGridMethod = psJob->pfnGDALGridMethod;
725✔
2646
    // Have a local copy of sExtraParameters since we want to modify
2647
    // nInitialFacetIdx.
2648
    GDALGridExtraParameters sExtraParameters = *psJob->psExtraParameters;
725✔
2649
    const GDALDataType eType = psJob->eType;
725✔
2650

2651
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
725✔
2652
    const int nLineSpace = nXSize * nDataTypeSize;
723✔
2653

2654
    for (GUInt32 nYPoint = nYStart; nYPoint < nYSize; nYPoint += nYStep)
21,606✔
2655
    {
2656
        const double dfYPoint = dfYMin + (nYPoint + 0.5) * dfDeltaY;
20,888✔
2657

2658
        for (GUInt32 nXPoint = 0; nXPoint < nXSize; nXPoint++)
4,605,660✔
2659
        {
2660
            const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
4,539,680✔
2661

2662
            if ((*pfnGDALGridMethod)(poOptions, nPoints, padfX, padfY, padfZ,
9,124,450✔
2663
                                     dfXPoint, dfYPoint, padfValues + nXPoint,
4,539,680✔
2664
                                     &sExtraParameters) != CE_None)
4,584,770✔
2665
            {
2666
                CPLError(CE_Failure, CPLE_AppDefined,
×
2667
                         "Gridding failed at X position %lu, Y position %lu",
2668
                         static_cast<long unsigned int>(nXPoint),
2669
                         static_cast<long unsigned int>(nYPoint));
2670
                *psJob->pbStop = TRUE;
×
2671
                if (pfnProgress != nullptr)
×
2672
                    pfnProgress(psJob);  // To notify the main thread.
×
2673
                break;
×
2674
            }
2675
        }
2676

2677
        GDALCopyWords(padfValues, GDT_Float64, sizeof(double),
65,982✔
2678
                      pabyData + nYPoint * nLineSpace, eType, nDataTypeSize,
65,982✔
2679
                      nXSize);
2680

2681
        if (*psJob->pbStop || (pfnProgress != nullptr && pfnProgress(psJob)))
20,874✔
2682
            break;
×
2683
    }
2684

2685
    CPLFree(padfValues);
718✔
2686
}
2687

2688
/************************************************************************/
2689
/*                        GDALGridContextCreate()                       */
2690
/************************************************************************/
2691

2692
struct GDALGridContext
2693
{
2694
    GDALGridAlgorithm eAlgorithm;
2695
    void *poOptions;
2696
    GDALGridFunction pfnGDALGridMethod;
2697

2698
    GUInt32 nPoints;
2699
    GDALGridPoint *pasGridPoints;
2700
    GDALGridXYArrays sXYArrays;
2701

2702
    GDALGridExtraParameters sExtraParameters;
2703
    double *padfX;
2704
    double *padfY;
2705
    double *padfZ;
2706
    bool bFreePadfXYZArrays;
2707

2708
    CPLWorkerThreadPool *poWorkerThreadPool;
2709
};
2710

2711
static void GDALGridContextCreateQuadTree(GDALGridContext *psContext);
2712

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

2754
GDALGridContext *GDALGridContextCreate(GDALGridAlgorithm eAlgorithm,
183✔
2755
                                       const void *poOptions, GUInt32 nPoints,
2756
                                       const double *padfX, const double *padfY,
2757
                                       const double *padfZ,
2758
                                       int bCallerWillKeepPointArraysAlive)
2759
{
2760
    CPLAssert(poOptions);
183✔
2761
    CPLAssert(padfX);
183✔
2762
    CPLAssert(padfY);
183✔
2763
    CPLAssert(padfZ);
183✔
2764
    bool bCreateQuadTree = false;
183✔
2765

2766
    const unsigned int nPointCountThreshold =
2767
        atoi(CPLGetConfigOption("GDAL_GRID_POINT_COUNT_THRESHOLD", "100"));
183✔
2768

2769
    // Starting address aligned on 32-byte boundary for AVX.
2770
    float *pafXAligned = nullptr;
183✔
2771
    float *pafYAligned = nullptr;
183✔
2772
    float *pafZAligned = nullptr;
183✔
2773

2774
    void *poOptionsNew = nullptr;
183✔
2775

2776
    GDALGridFunction pfnGDALGridMethod = nullptr;
183✔
2777

2778
    switch (eAlgorithm)
183✔
2779
    {
2780
        case GGA_InverseDistanceToAPower:
44✔
2781
        {
2782
            const auto poOptionsOld =
44✔
2783
                static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
2784
                    poOptions);
2785
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
44✔
2786
            {
2787
                CPLError(CE_Failure, CPLE_AppDefined,
×
2788
                         "Wrong value of nSizeOfStructure member");
2789
                return nullptr;
×
2790
            }
2791
            poOptionsNew =
2792
                CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
44✔
2793
            memcpy(poOptionsNew, poOptions,
44✔
2794
                   sizeof(GDALGridInverseDistanceToAPowerOptions));
2795

2796
            const GDALGridInverseDistanceToAPowerOptions *const poPower =
44✔
2797
                static_cast<const GDALGridInverseDistanceToAPowerOptions *>(
2798
                    poOptions);
2799
            if (poPower->dfRadius1 == 0.0 && poPower->dfRadius2 == 0.0)
44✔
2800
            {
2801
                const double dfPower = poPower->dfPower;
35✔
2802
                const double dfSmoothing = poPower->dfSmoothing;
35✔
2803

2804
                pfnGDALGridMethod = GDALGridInverseDistanceToAPowerNoSearch;
35✔
2805
                if (dfPower == 2.0 && dfSmoothing == 0.0)
35✔
2806
                {
2807
#ifdef HAVE_AVX_AT_COMPILE_TIME
2808

2809
                    if (CPLTestBool(
33✔
2810
                            CPLGetConfigOption("GDAL_USE_AVX", "YES")) &&
60✔
2811
                        CPLHaveRuntimeAVX())
27✔
2812
                    {
2813
                        pafXAligned = static_cast<float *>(
2814
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
27✔
2815
                                                            nPoints));
2816
                        pafYAligned = static_cast<float *>(
2817
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
27✔
2818
                                                            nPoints));
2819
                        pafZAligned = static_cast<float *>(
2820
                            VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(float) *
27✔
2821
                                                            nPoints));
2822
                        if (pafXAligned != nullptr && pafYAligned != nullptr &&
27✔
2823
                            pafZAligned != nullptr)
2824
                        {
2825
                            CPLDebug("GDAL_GRID",
27✔
2826
                                     "Using AVX optimized version");
2827
                            pfnGDALGridMethod =
27✔
2828
                                GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX;
2829
                            for (GUInt32 i = 0; i < nPoints; i++)
1,335✔
2830
                            {
2831
                                pafXAligned[i] = static_cast<float>(padfX[i]);
1,308✔
2832
                                pafYAligned[i] = static_cast<float>(padfY[i]);
1,308✔
2833
                                pafZAligned[i] = static_cast<float>(padfZ[i]);
1,308✔
2834
                            }
27✔
2835
                        }
2836
                        else
2837
                        {
2838
                            VSIFree(pafXAligned);
×
2839
                            VSIFree(pafYAligned);
×
2840
                            VSIFree(pafZAligned);
×
2841
                            pafXAligned = nullptr;
×
2842
                            pafYAligned = nullptr;
×
2843
                            pafZAligned = nullptr;
×
2844
                        }
2845
                    }
2846
#endif
2847

2848
#ifdef HAVE_SSE_AT_COMPILE_TIME
2849

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

2916
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
22✔
2917
                poOptionsOld->nMaxPointsPerQuadrant != 0)
12✔
2918
            {
2919
                pfnGDALGridMethod =
12✔
2920
                    GDALGridInverseDistanceToAPowerNearestNeighborPerQuadrant;
2921
            }
2922
            else
2923
            {
2924
                pfnGDALGridMethod =
10✔
2925
                    GDALGridInverseDistanceToAPowerNearestNeighbor;
2926
            }
2927
            bCreateQuadTree = true;
22✔
2928
            break;
22✔
2929
        }
2930
        case GGA_MovingAverage:
26✔
2931
        {
2932
            const auto poOptionsOld =
26✔
2933
                static_cast<const GDALGridMovingAverageOptions *>(poOptions);
2934
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
26✔
2935
            {
2936
                CPLError(CE_Failure, CPLE_AppDefined,
×
2937
                         "Wrong value of nSizeOfStructure member");
2938
                return nullptr;
×
2939
            }
2940
            poOptionsNew = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
26✔
2941
            memcpy(poOptionsNew, poOptions,
26✔
2942
                   sizeof(GDALGridMovingAverageOptions));
2943

2944
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
26✔
2945
                poOptionsOld->nMaxPointsPerQuadrant != 0)
18✔
2946
            {
2947
                pfnGDALGridMethod = GDALGridMovingAveragePerQuadrant;
9✔
2948
                bCreateQuadTree = true;
9✔
2949
            }
2950
            else
2951
            {
2952
                pfnGDALGridMethod = GDALGridMovingAverage;
17✔
2953
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
17✔
2954
                                   poOptionsOld->dfAngle == 0.0 &&
22✔
2955
                                   (poOptionsOld->dfRadius1 > 0.0 ||
5✔
2956
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
2957
            }
2958
            break;
26✔
2959
        }
2960
        case GGA_NearestNeighbor:
21✔
2961
        {
2962
            const auto poOptionsOld =
21✔
2963
                static_cast<const GDALGridNearestNeighborOptions *>(poOptions);
2964
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
21✔
2965
            {
2966
                CPLError(CE_Failure, CPLE_AppDefined,
×
2967
                         "Wrong value of nSizeOfStructure member");
2968
                return nullptr;
×
2969
            }
2970
            poOptionsNew = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
21✔
2971
            memcpy(poOptionsNew, poOptions,
21✔
2972
                   sizeof(GDALGridNearestNeighborOptions));
2973

2974
            pfnGDALGridMethod = GDALGridNearestNeighbor;
21✔
2975
            bCreateQuadTree = (nPoints > nPointCountThreshold &&
34✔
2976
                               poOptionsOld->dfAngle == 0.0 &&
34✔
2977
                               (poOptionsOld->dfRadius1 > 0.0 ||
13✔
2978
                                poOptionsOld->dfRadius2 > 0.0));
5✔
2979
            break;
21✔
2980
        }
2981
        case GGA_MetricMinimum:
18✔
2982
        {
2983
            const auto poOptionsOld =
18✔
2984
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
2985
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
18✔
2986
            {
2987
                CPLError(CE_Failure, CPLE_AppDefined,
×
2988
                         "Wrong value of nSizeOfStructure member");
2989
                return nullptr;
×
2990
            }
2991
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
18✔
2992
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
18✔
2993

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

3023
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
12✔
3024
                poOptionsOld->nMaxPointsPerQuadrant != 0)
7✔
3025
            {
3026
                pfnGDALGridMethod = GDALGridDataMetricMaximumPerQuadrant;
5✔
3027
                bCreateQuadTree = true;
5✔
3028
            }
3029
            else
3030
            {
3031
                pfnGDALGridMethod = GDALGridDataMetricMaximum;
7✔
3032
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
7✔
3033
                                   poOptionsOld->dfAngle == 0.0 &&
11✔
3034
                                   (poOptionsOld->dfRadius1 > 0.0 ||
4✔
3035
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3036
            }
3037

3038
            break;
12✔
3039
        }
3040
        case GGA_MetricRange:
9✔
3041
        {
3042
            const auto poOptionsOld =
9✔
3043
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3044
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
9✔
3045
            {
3046
                CPLError(CE_Failure, CPLE_AppDefined,
×
3047
                         "Wrong value of nSizeOfStructure member");
3048
                return nullptr;
×
3049
            }
3050
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
9✔
3051
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
9✔
3052

3053
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
9✔
3054
                poOptionsOld->nMaxPointsPerQuadrant != 0)
4✔
3055
            {
3056
                pfnGDALGridMethod = GDALGridDataMetricRangePerQuadrant;
5✔
3057
                bCreateQuadTree = true;
5✔
3058
            }
3059
            else
3060
            {
3061
                pfnGDALGridMethod = GDALGridDataMetricRange;
4✔
3062
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
4✔
3063
                                   poOptionsOld->dfAngle == 0.0 &&
7✔
3064
                                   (poOptionsOld->dfRadius1 > 0.0 ||
3✔
3065
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3066
            }
3067

3068
            break;
9✔
3069
        }
3070
        case GGA_MetricCount:
11✔
3071
        {
3072
            const auto poOptionsOld =
11✔
3073
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3074
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
11✔
3075
            {
3076
                CPLError(CE_Failure, CPLE_AppDefined,
×
3077
                         "Wrong value of nSizeOfStructure member");
3078
                return nullptr;
×
3079
            }
3080
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
11✔
3081
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
11✔
3082

3083
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
11✔
3084
                poOptionsOld->nMaxPointsPerQuadrant != 0)
6✔
3085
            {
3086
                pfnGDALGridMethod = GDALGridDataMetricCountPerQuadrant;
5✔
3087
                bCreateQuadTree = true;
5✔
3088
            }
3089
            else
3090
            {
3091
                pfnGDALGridMethod = GDALGridDataMetricCount;
6✔
3092
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
6✔
3093
                                   poOptionsOld->dfAngle == 0.0 &&
11✔
3094
                                   (poOptionsOld->dfRadius1 > 0.0 ||
5✔
3095
                                    poOptionsOld->dfRadius2 > 0.0));
×
3096
            }
3097

3098
            break;
11✔
3099
        }
3100
        case GGA_MetricAverageDistance:
9✔
3101
        {
3102
            const auto poOptionsOld =
9✔
3103
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3104
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
9✔
3105
            {
3106
                CPLError(CE_Failure, CPLE_AppDefined,
×
3107
                         "Wrong value of nSizeOfStructure member");
3108
                return nullptr;
×
3109
            }
3110
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
9✔
3111
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
9✔
3112

3113
            if (poOptionsOld->nMinPointsPerQuadrant != 0 ||
9✔
3114
                poOptionsOld->nMaxPointsPerQuadrant != 0)
4✔
3115
            {
3116
                pfnGDALGridMethod =
5✔
3117
                    GDALGridDataMetricAverageDistancePerQuadrant;
3118
                bCreateQuadTree = true;
5✔
3119
            }
3120
            else
3121
            {
3122
                pfnGDALGridMethod = GDALGridDataMetricAverageDistance;
4✔
3123
                bCreateQuadTree = (nPoints > nPointCountThreshold &&
4✔
3124
                                   poOptionsOld->dfAngle == 0.0 &&
7✔
3125
                                   (poOptionsOld->dfRadius1 > 0.0 ||
3✔
3126
                                    poOptionsOld->dfRadius2 > 0.0));
1✔
3127
            }
3128

3129
            break;
9✔
3130
        }
3131
        case GGA_MetricAverageDistancePts:
4✔
3132
        {
3133
            const auto poOptionsOld =
4✔
3134
                static_cast<const GDALGridDataMetricsOptions *>(poOptions);
3135
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
4✔
3136
            {
3137
                CPLError(CE_Failure, CPLE_AppDefined,
×
3138
                         "Wrong value of nSizeOfStructure member");
3139
                return nullptr;
×
3140
            }
3141
            poOptionsNew = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
4✔
3142
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridDataMetricsOptions));
4✔
3143

3144
            pfnGDALGridMethod = GDALGridDataMetricAverageDistancePts;
4✔
3145
            bCreateQuadTree = (nPoints > nPointCountThreshold &&
7✔
3146
                               poOptionsOld->dfAngle == 0.0 &&
6✔
3147
                               (poOptionsOld->dfRadius1 > 0.0 ||
2✔
3148
                                poOptionsOld->dfRadius2 > 0.0));
×
3149

3150
            break;
4✔
3151
        }
3152
        case GGA_Linear:
7✔
3153
        {
3154
            const auto poOptionsOld =
7✔
3155
                static_cast<const GDALGridLinearOptions *>(poOptions);
3156
            if (poOptionsOld->nSizeOfStructure != sizeof(*poOptionsOld))
7✔
3157
            {
3158
                CPLError(CE_Failure, CPLE_AppDefined,
×
3159
                         "Wrong value of nSizeOfStructure member");
3160
                return nullptr;
×
3161
            }
3162
            poOptionsNew = CPLMalloc(sizeof(GDALGridLinearOptions));
7✔
3163
            memcpy(poOptionsNew, poOptions, sizeof(GDALGridLinearOptions));
7✔
3164

3165
            pfnGDALGridMethod = GDALGridLinear;
7✔
3166
            break;
7✔
3167
        }
3168
        default:
×
3169
            CPLError(CE_Failure, CPLE_IllegalArg,
×
3170
                     "GDAL does not support gridding method %d", eAlgorithm);
3171
            return nullptr;
×
3172
    }
3173

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

3219
    /* -------------------------------------------------------------------- */
3220
    /*  Create quadtree if requested and possible.                          */
3221
    /* -------------------------------------------------------------------- */
3222
    if (bCreateQuadTree)
183✔
3223
    {
3224
        GDALGridContextCreateQuadTree(psContext);
88✔
3225
        if (psContext->sExtraParameters.hQuadTree == nullptr &&
88✔
3226
            (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor ||
×
3227
             pfnGDALGridMethod == GDALGridMovingAveragePerQuadrant))
3228
        {
3229
            // shouldn't happen unless memory allocation failure occurs
3230
            GDALGridContextFree(psContext);
×
3231
            return nullptr;
×
3232
        }
3233
    }
3234

3235
    /* -------------------------------------------------------------------- */
3236
    /*  Pre-compute extra parameters in GDALGridExtraParameters              */
3237
    /* -------------------------------------------------------------------- */
3238
    if (eAlgorithm == GGA_InverseDistanceToAPowerNearestNeighbor)
183✔
3239
    {
3240
        const double dfPower =
22✔
3241
            static_cast<
3242
                const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3243
                poOptions)
3244
                ->dfPower;
3245
        psContext->sExtraParameters.dfPowerDiv2PreComp = dfPower / 2;
22✔
3246

3247
        const double dfRadius =
22✔
3248
            static_cast<
3249
                const GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3250
                poOptions)
3251
                ->dfRadius;
3252
        psContext->sExtraParameters.dfRadiusPower2PreComp = pow(dfRadius, 2);
22✔
3253
    }
3254

3255
    if (eAlgorithm == GGA_Linear)
183✔
3256
    {
3257
        psContext->sExtraParameters.psTriangulation =
7✔
3258
            GDALTriangulationCreateDelaunay(nPoints, padfX, padfY);
7✔
3259
        if (psContext->sExtraParameters.psTriangulation == nullptr)
7✔
3260
        {
3261
            GDALGridContextFree(psContext);
×
3262
            return nullptr;
×
3263
        }
3264
        GDALTriangulationComputeBarycentricCoefficients(
7✔
3265
            psContext->sExtraParameters.psTriangulation, padfX, padfY);
3266
    }
3267

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

3295
    return psContext;
183✔
3296
}
3297

3298
/************************************************************************/
3299
/*                      GDALGridContextCreateQuadTree()                 */
3300
/************************************************************************/
3301

3302
void GDALGridContextCreateQuadTree(GDALGridContext *psContext)
94✔
3303
{
3304
    const GUInt32 nPoints = psContext->nPoints;
94✔
3305
    psContext->pasGridPoints = static_cast<GDALGridPoint *>(
94✔
3306
        VSI_MALLOC2_VERBOSE(nPoints, sizeof(GDALGridPoint)));
94✔
3307
    if (psContext->pasGridPoints != nullptr)
94✔
3308
    {
3309
        const double *const padfX = psContext->padfX;
94✔
3310
        const double *const padfY = psContext->padfY;
94✔
3311

3312
        // Determine point extents.
3313
        CPLRectObj sRect;
3314
        sRect.minx = padfX[0];
94✔
3315
        sRect.miny = padfY[0];
94✔
3316
        sRect.maxx = padfX[0];
94✔
3317
        sRect.maxy = padfY[0];
94✔
3318
        for (GUInt32 i = 1; i < nPoints; i++)
28,131✔
3319
        {
3320
            if (padfX[i] < sRect.minx)
28,037✔
3321
                sRect.minx = padfX[i];
40✔
3322
            if (padfY[i] < sRect.miny)
28,037✔
3323
                sRect.miny = padfY[i];
788✔
3324
            if (padfX[i] > sRect.maxx)
28,037✔
3325
                sRect.maxx = padfX[i];
794✔
3326
            if (padfY[i] > sRect.maxy)
28,037✔
3327
                sRect.maxy = padfY[i];
30✔
3328
        }
3329

3330
        // Initial value for search radius is the typical dimension of a
3331
        // "pixel" of the point array (assuming rather uniform distribution).
3332
        psContext->sExtraParameters.dfInitialSearchRadius = sqrt(
94✔
3333
            (sRect.maxx - sRect.minx) * (sRect.maxy - sRect.miny) / nPoints);
94✔
3334

3335
        psContext->sExtraParameters.hQuadTree =
94✔
3336
            CPLQuadTreeCreate(&sRect, GDALGridGetPointBounds);
94✔
3337

3338
        for (GUInt32 i = 0; i < nPoints; i++)
28,225✔
3339
        {
3340
            psContext->pasGridPoints[i].psXYArrays = &(psContext->sXYArrays);
28,131✔
3341
            psContext->pasGridPoints[i].i = i;
28,131✔
3342
            CPLQuadTreeInsert(psContext->sExtraParameters.hQuadTree,
28,131✔
3343
                              psContext->pasGridPoints + i);
28,131✔
3344
        }
3345
    }
3346
}
94✔
3347

3348
/************************************************************************/
3349
/*                        GDALGridContextFree()                         */
3350
/************************************************************************/
3351

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

3383
/************************************************************************/
3384
/*                        GDALGridContextProcess()                      */
3385
/************************************************************************/
3386

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

3413
CPLErr GDALGridContextProcess(GDALGridContext *psContext, double dfXMin,
183✔
3414
                              double dfXMax, double dfYMin, double dfYMax,
3415
                              GUInt32 nXSize, GUInt32 nYSize,
3416
                              GDALDataType eType, void *pData,
3417
                              GDALProgressFunc pfnProgress, void *pProgressArg)
3418
{
3419
    CPLAssert(psContext);
183✔
3420
    CPLAssert(pData);
183✔
3421

3422
    if (nXSize == 0 || nYSize == 0)
183✔
3423
    {
3424
        CPLError(CE_Failure, CPLE_IllegalArg,
×
3425
                 "Output raster dimensions should have non-zero size.");
3426
        return CE_Failure;
×
3427
    }
3428

3429
    const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
183✔
3430
    const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
183✔
3431

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

3447
            if (!GDALTriangulationFindFacetDirected(
262✔
3448
                    psContext->sExtraParameters.psTriangulation, nStartLeft,
262✔
3449
                    dfXPointMin, dfYPoint, &nStartLeft))
3450
            {
3451
                bNeedNearest = true;
6✔
3452
            }
3453
            if (!GDALTriangulationFindFacetDirected(
262✔
3454
                    psContext->sExtraParameters.psTriangulation, nStartRight,
262✔
3455
                    dfXPointMax, dfYPoint, &nStartRight))
3456
            {
3457
                bNeedNearest = true;
6✔
3458
            }
3459
        }
3460
        int nStartTop = 0;
7✔
3461
        int nStartBottom = 0;
7✔
3462
        const double dfYPointMin = dfYMin + (0 + 0.5) * dfDeltaY;
7✔
3463
        const double dfYPointMax = dfYMin + (nYSize - 1 + 0.5) * dfDeltaY;
7✔
3464
        for (GUInt32 nXPoint = 1; !bNeedNearest && nXPoint + 1 < nXSize;
261✔
3465
             nXPoint++)
3466
        {
3467
            const double dfXPoint = dfXMin + (nXPoint + 0.5) * dfDeltaX;
254✔
3468

3469
            if (!GDALTriangulationFindFacetDirected(
254✔
3470
                    psContext->sExtraParameters.psTriangulation, nStartTop,
254✔
3471
                    dfXPoint, dfYPointMin, &nStartTop))
3472
            {
3473
                bNeedNearest = true;
×
3474
            }
3475
            if (!GDALTriangulationFindFacetDirected(
254✔
3476
                    psContext->sExtraParameters.psTriangulation, nStartBottom,
254✔
3477
                    dfXPoint, dfYPointMax, &nStartBottom))
3478
            {
3479
                bNeedNearest = true;
×
3480
            }
3481
        }
3482
        if (bNeedNearest)
7✔
3483
        {
3484
            CPLDebug("GDAL_GRID", "Will need nearest neighbour");
6✔
3485
            GDALGridContextCreateQuadTree(psContext);
6✔
3486
        }
3487
    }
3488

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

3518
    if (psContext->poWorkerThreadPool == nullptr)
183✔
3519
    {
3520
        if (sJob.pfnRealProgress != nullptr &&
2✔
3521
            sJob.pfnRealProgress != GDALDummyProgress)
2✔
3522
        {
3523
            sJob.pfnProgress = GDALGridProgressMonoThread;
2✔
3524
        }
3525

3526
        GDALGridJobProcess(&sJob);
2✔
3527
    }
3528
    else
3529
    {
3530
        int nThreads = psContext->poWorkerThreadPool->GetThreadCount();
181✔
3531
        GDALGridJob *pasJobs = static_cast<GDALGridJob *>(
3532
            CPLMalloc(sizeof(GDALGridJob) * nThreads));
181✔
3533

3534
        sJob.nYStep = nThreads;
181✔
3535
        sJob.hCondMutex = CPLCreateMutex(); /* and  implicitly take the mutex */
181✔
3536
        sJob.hCond = CPLCreateCond();
181✔
3537
        sJob.pfnProgress = GDALGridProgressMultiThread;
181✔
3538

3539
        /* --------------------------------------------------------------------
3540
         */
3541
        /*      Start threads. */
3542
        /* --------------------------------------------------------------------
3543
         */
3544
        for (int i = 0; i < nThreads && !bStop; i++)
905✔
3545
        {
3546
            memcpy(&pasJobs[i], &sJob, sizeof(GDALGridJob));
724✔
3547
            pasJobs[i].nYStart = i;
724✔
3548
            psContext->poWorkerThreadPool->SubmitJob(GDALGridJobProcess,
724✔
3549
                                                     &pasJobs[i]);
724✔
3550
        }
3551

3552
        /* --------------------------------------------------------------------
3553
         */
3554
        /*      Report progress. */
3555
        /* --------------------------------------------------------------------
3556
         */
3557
        while (*(sJob.pnCounter) < static_cast<int>(nYSize) && !bStop)
8,797✔
3558
        {
3559
            CPLCondWait(sJob.hCond, sJob.hCondMutex);
8,616✔
3560

3561
            int nLocalCounter = *(sJob.pnCounter);
8,616✔
3562
            CPLReleaseMutex(sJob.hCondMutex);
8,616✔
3563

3564
            if (pfnProgress != nullptr &&
17,225✔
3565
                !pfnProgress(nLocalCounter / static_cast<double>(nYSize), "",
8,609✔
3566
                             pProgressArg))
3567
            {
3568
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
×
3569
                bStop = TRUE;
×
3570
            }
3571

3572
            CPLAcquireMutex(sJob.hCondMutex, 1.0);
8,616✔
3573
        }
3574

3575
        // Release mutex before joining threads, otherwise they will dead-lock
3576
        // forever in GDALGridProgressMultiThread().
3577
        CPLReleaseMutex(sJob.hCondMutex);
181✔
3578

3579
        /* --------------------------------------------------------------------
3580
         */
3581
        /*      Wait for all threads to complete and finish. */
3582
        /* --------------------------------------------------------------------
3583
         */
3584
        psContext->poWorkerThreadPool->WaitCompletion();
181✔
3585

3586
        CPLFree(pasJobs);
181✔
3587
        CPLDestroyCond(sJob.hCond);
181✔
3588
        CPLDestroyMutex(sJob.hCondMutex);
181✔
3589
    }
3590

3591
    return bStop ? CE_Failure : CE_None;
183✔
3592
}
3593

3594
/************************************************************************/
3595
/*                            GDALGridCreate()                          */
3596
/************************************************************************/
3597

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

3649
CPLErr GDALGridCreate(GDALGridAlgorithm eAlgorithm, const void *poOptions,
5✔
3650
                      GUInt32 nPoints, const double *padfX, const double *padfY,
3651
                      const double *padfZ, double dfXMin, double dfXMax,
3652
                      double dfYMin, double dfYMax, GUInt32 nXSize,
3653
                      GUInt32 nYSize, GDALDataType eType, void *pData,
3654
                      GDALProgressFunc pfnProgress, void *pProgressArg)
3655
{
3656
    GDALGridContext *psContext = GDALGridContextCreate(
5✔
3657
        eAlgorithm, poOptions, nPoints, padfX, padfY, padfZ, TRUE);
3658
    CPLErr eErr = CE_Failure;
5✔
3659
    if (psContext)
5✔
3660
    {
3661
        eErr = GDALGridContextProcess(psContext, dfXMin, dfXMax, dfYMin, dfYMax,
5✔
3662
                                      nXSize, nYSize, eType, pData, pfnProgress,
3663
                                      pProgressArg);
3664
    }
3665

3666
    GDALGridContextFree(psContext);
5✔
3667
    return eErr;
5✔
3668
}
3669

3670
/************************************************************************/
3671
/*                   GDALGridParseAlgorithmAndOptions()                 */
3672
/************************************************************************/
3673

3674
/** Translates mnemonic gridding algorithm names into GDALGridAlgorithm code,
3675
 * parse control parameters and assign defaults.
3676
 */
3677
CPLErr GDALGridParseAlgorithmAndOptions(const char *pszAlgorithm,
383✔
3678
                                        GDALGridAlgorithm *peAlgorithm,
3679
                                        void **ppOptions)
3680
{
3681
    CPLAssert(pszAlgorithm);
383✔
3682
    CPLAssert(peAlgorithm);
383✔
3683
    CPLAssert(ppOptions);
383✔
3684

3685
    *ppOptions = nullptr;
383✔
3686

3687
    char **papszParams = CSLTokenizeString2(pszAlgorithm, ":", FALSE);
383✔
3688

3689
    if (CSLCount(papszParams) < 1)
383✔
3690
    {
3691
        CSLDestroy(papszParams);
×
3692
        return CE_Failure;
×
3693
    }
3694

3695
    if (EQUAL(papszParams[0], szAlgNameInvDist))
383✔
3696
    {
3697
        if (CSLFetchNameValue(papszParams, "min_points_per_quadrant") ||
495✔
3698
            CSLFetchNameValue(papszParams, "max_points_per_quadrant"))
246✔
3699
        {
3700
            // Remap invdist to invdistnn if per quadrant is required
3701
            if (CSLFetchNameValue(papszParams, "radius") == nullptr)
4✔
3702
            {
3703
                const double dfRadius1 =
3704
                    CPLAtofM(CSLFetchNameValueDef(papszParams, "radius1", "1"));
×
3705
                const double dfRadius2 =
3706
                    CPLAtofM(CSLFetchNameValueDef(papszParams, "radius2", "1"));
×
3707
                if (dfRadius1 != dfRadius2)
×
3708
                {
3709
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3710
                             "radius1 != radius2 not supported when "
3711
                             "min_points_per_quadrant and/or "
3712
                             "max_points_per_quadrant is specified");
3713
                    CSLDestroy(papszParams);
×
3714
                    return CE_Failure;
×
3715
                }
3716
            }
3717

3718
            if (CPLAtofM(CSLFetchNameValueDef(papszParams, "angle", "0")) != 0)
4✔
3719
            {
3720
                CPLError(CE_Failure, CPLE_NotSupported,
×
3721
                         "angle != 0 not supported when "
3722
                         "min_points_per_quadrant and/or "
3723
                         "max_points_per_quadrant is specified");
3724
                CSLDestroy(papszParams);
×
3725
                return CE_Failure;
×
3726
            }
3727

3728
            char **papszNewParams = CSLAddString(nullptr, "invdistnn");
4✔
3729
            if (CSLFetchNameValue(papszParams, "radius") == nullptr)
4✔
3730
            {
3731
                papszNewParams = CSLSetNameValue(
×
3732
                    papszNewParams, "radius",
3733
                    CSLFetchNameValueDef(papszParams, "radius1", "1"));
3734
            }
3735
            for (const char *pszItem :
32✔
3736
                 {"radius", "power", "smoothing", "max_points", "min_points",
3737
                  "nodata", "min_points_per_quadrant",
3738
                  "max_points_per_quadrant"})
36✔
3739
            {
3740
                const char *pszValue = CSLFetchNameValue(papszParams, pszItem);
32✔
3741
                if (pszValue)
32✔
3742
                    papszNewParams =
3743
                        CSLSetNameValue(papszNewParams, pszItem, pszValue);
19✔
3744
            }
3745
            CSLDestroy(papszParams);
4✔
3746
            papszParams = papszNewParams;
4✔
3747

3748
            *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
4✔
3749
        }
3750
        else
3751
        {
3752
            *peAlgorithm = GGA_InverseDistanceToAPower;
245✔
3753
        }
3754
    }
3755
    else if (EQUAL(papszParams[0], szAlgNameInvDistNearestNeighbor))
134✔
3756
    {
3757
        *peAlgorithm = GGA_InverseDistanceToAPowerNearestNeighbor;
18✔
3758
    }
3759
    else if (EQUAL(papszParams[0], szAlgNameAverage))
116✔
3760
    {
3761
        *peAlgorithm = GGA_MovingAverage;
26✔
3762
    }
3763
    else if (EQUAL(papszParams[0], szAlgNameNearest))
90✔
3764
    {
3765
        *peAlgorithm = GGA_NearestNeighbor;
19✔
3766
    }
3767
    else if (EQUAL(papszParams[0], szAlgNameMinimum))
71✔
3768
    {
3769
        *peAlgorithm = GGA_MetricMinimum;
18✔
3770
    }
3771
    else if (EQUAL(papszParams[0], szAlgNameMaximum))
53✔
3772
    {
3773
        *peAlgorithm = GGA_MetricMaximum;
12✔
3774
    }
3775
    else if (EQUAL(papszParams[0], szAlgNameRange))
41✔
3776
    {
3777
        *peAlgorithm = GGA_MetricRange;
9✔
3778
    }
3779
    else if (EQUAL(papszParams[0], szAlgNameCount))
32✔
3780
    {
3781
        *peAlgorithm = GGA_MetricCount;
11✔
3782
    }
3783
    else if (EQUAL(papszParams[0], szAlgNameAverageDistance))
21✔
3784
    {
3785
        *peAlgorithm = GGA_MetricAverageDistance;
9✔
3786
    }
3787
    else if (EQUAL(papszParams[0], szAlgNameAverageDistancePts))
12✔
3788
    {
3789
        *peAlgorithm = GGA_MetricAverageDistancePts;
4✔
3790
    }
3791
    else if (EQUAL(papszParams[0], szAlgNameLinear))
8✔
3792
    {
3793
        *peAlgorithm = GGA_Linear;
7✔
3794
    }
3795
    else
3796
    {
3797
        CPLError(CE_Failure, CPLE_IllegalArg,
1✔
3798
                 "Unsupported gridding method \"%s\"", papszParams[0]);
3799
        CSLDestroy(papszParams);
1✔
3800
        return CE_Failure;
1✔
3801
    }
3802

3803
    /* -------------------------------------------------------------------- */
3804
    /*      Parse algorithm parameters and assign defaults.                 */
3805
    /* -------------------------------------------------------------------- */
3806
    const char *const *papszKnownOptions = nullptr;
382✔
3807

3808
    switch (*peAlgorithm)
382✔
3809
    {
3810
        case GGA_InverseDistanceToAPower:
245✔
3811
        default:
3812
        {
3813
            *ppOptions =
245✔
3814
                CPLMalloc(sizeof(GDALGridInverseDistanceToAPowerOptions));
245✔
3815

3816
            GDALGridInverseDistanceToAPowerOptions *const poPowerOpts =
245✔
3817
                static_cast<GDALGridInverseDistanceToAPowerOptions *>(
3818
                    *ppOptions);
3819

3820
            poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
245✔
3821

3822
            const char *pszValue = CSLFetchNameValue(papszParams, "power");
245✔
3823
            poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
245✔
3824

3825
            pszValue = CSLFetchNameValue(papszParams, "smoothing");
245✔
3826
            poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
245✔
3827

3828
            pszValue = CSLFetchNameValue(papszParams, "radius");
245✔
3829
            if (pszValue)
245✔
3830
            {
3831
                poPowerOpts->dfRadius1 = CPLAtofM(pszValue);
5✔
3832
                poPowerOpts->dfRadius2 = poPowerOpts->dfRadius1;
5✔
3833
            }
3834
            else
3835
            {
3836
                pszValue = CSLFetchNameValue(papszParams, "radius1");
240✔
3837
                poPowerOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
240✔
3838

3839
                pszValue = CSLFetchNameValue(papszParams, "radius2");
240✔
3840
                poPowerOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
240✔
3841
            }
3842

3843
            pszValue = CSLFetchNameValue(papszParams, "angle");
245✔
3844
            poPowerOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
245✔
3845

3846
            pszValue = CSLFetchNameValue(papszParams, "max_points");
245✔
3847
            poPowerOpts->nMaxPoints =
245✔
3848
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
245✔
3849

3850
            pszValue = CSLFetchNameValue(papszParams, "min_points");
245✔
3851
            poPowerOpts->nMinPoints =
245✔
3852
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
245✔
3853

3854
            pszValue = CSLFetchNameValue(papszParams, "nodata");
245✔
3855
            poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
245✔
3856

3857
            static const char *const apszKnownOptions[] = {
3858
                "power", "smoothing",  "radius",     "radius1", "radius2",
3859
                "angle", "max_points", "min_points", "nodata",  nullptr};
3860
            papszKnownOptions = apszKnownOptions;
245✔
3861

3862
            break;
245✔
3863
        }
3864
        case GGA_InverseDistanceToAPowerNearestNeighbor:
22✔
3865
        {
3866
            *ppOptions = CPLMalloc(
22✔
3867
                sizeof(GDALGridInverseDistanceToAPowerNearestNeighborOptions));
3868

3869
            GDALGridInverseDistanceToAPowerNearestNeighborOptions
3870
                *const poPowerOpts = static_cast<
22✔
3871
                    GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
3872
                    *ppOptions);
3873

3874
            poPowerOpts->nSizeOfStructure = sizeof(*poPowerOpts);
22✔
3875

3876
            const char *pszValue = CSLFetchNameValue(papszParams, "power");
22✔
3877
            poPowerOpts->dfPower = pszValue ? CPLAtofM(pszValue) : 2.0;
22✔
3878

3879
            pszValue = CSLFetchNameValue(papszParams, "smoothing");
22✔
3880
            poPowerOpts->dfSmoothing = pszValue ? CPLAtofM(pszValue) : 0.0;
22✔
3881

3882
            pszValue = CSLFetchNameValue(papszParams, "radius");
22✔
3883
            poPowerOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : 1.0;
22✔
3884
            if (!(poPowerOpts->dfRadius > 0))
22✔
3885
            {
3886
                CPLError(CE_Failure, CPLE_IllegalArg,
×
3887
                         "Radius value should be strictly positive");
3888
                CSLDestroy(papszParams);
×
3889
                return CE_Failure;
×
3890
            }
3891

3892
            pszValue = CSLFetchNameValue(papszParams, "max_points");
22✔
3893
            poPowerOpts->nMaxPoints =
22✔
3894
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 12);
22✔
3895

3896
            pszValue = CSLFetchNameValue(papszParams, "min_points");
22✔
3897
            poPowerOpts->nMinPoints =
22✔
3898
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
22✔
3899

3900
            pszValue = CSLFetchNameValue(papszParams, "nodata");
22✔
3901
            poPowerOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
22✔
3902

3903
            pszValue =
3904
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
22✔
3905
            poPowerOpts->nMinPointsPerQuadrant =
22✔
3906
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
22✔
3907

3908
            pszValue =
3909
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
22✔
3910
            poPowerOpts->nMaxPointsPerQuadrant =
22✔
3911
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
22✔
3912

3913
            static const char *const apszKnownOptions[] = {
3914
                "power",
3915
                "smoothing",
3916
                "radius",
3917
                "max_points",
3918
                "min_points",
3919
                "nodata",
3920
                "min_points_per_quadrant",
3921
                "max_points_per_quadrant",
3922
                nullptr};
3923
            papszKnownOptions = apszKnownOptions;
22✔
3924

3925
            break;
22✔
3926
        }
3927
        case GGA_MovingAverage:
26✔
3928
        {
3929
            *ppOptions = CPLMalloc(sizeof(GDALGridMovingAverageOptions));
26✔
3930

3931
            GDALGridMovingAverageOptions *const poAverageOpts =
26✔
3932
                static_cast<GDALGridMovingAverageOptions *>(*ppOptions);
3933

3934
            poAverageOpts->nSizeOfStructure = sizeof(*poAverageOpts);
26✔
3935

3936
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
26✔
3937
            if (pszValue)
26✔
3938
            {
3939
                poAverageOpts->dfRadius1 = CPLAtofM(pszValue);
14✔
3940
                poAverageOpts->dfRadius2 = poAverageOpts->dfRadius1;
14✔
3941
            }
3942
            else
3943
            {
3944
                pszValue = CSLFetchNameValue(papszParams, "radius1");
12✔
3945
                poAverageOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
12✔
3946

3947
                pszValue = CSLFetchNameValue(papszParams, "radius2");
12✔
3948
                poAverageOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
12✔
3949
            }
3950

3951
            pszValue = CSLFetchNameValue(papszParams, "angle");
26✔
3952
            poAverageOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
26✔
3953

3954
            pszValue = CSLFetchNameValue(papszParams, "min_points");
26✔
3955
            poAverageOpts->nMinPoints =
26✔
3956
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
26✔
3957

3958
            pszValue = CSLFetchNameValue(papszParams, "max_points");
26✔
3959
            poAverageOpts->nMaxPoints =
26✔
3960
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
26✔
3961

3962
            pszValue = CSLFetchNameValue(papszParams, "nodata");
26✔
3963
            poAverageOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
26✔
3964

3965
            pszValue =
3966
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
26✔
3967
            poAverageOpts->nMinPointsPerQuadrant =
26✔
3968
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
26✔
3969

3970
            pszValue =
3971
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
26✔
3972
            poAverageOpts->nMaxPointsPerQuadrant =
26✔
3973
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
26✔
3974

3975
            if (poAverageOpts->nMinPointsPerQuadrant != 0 ||
26✔
3976
                poAverageOpts->nMaxPointsPerQuadrant != 0)
18✔
3977
            {
3978
                if (!(poAverageOpts->dfRadius1 > 0) ||
9✔
3979
                    !(poAverageOpts->dfRadius2 > 0))
9✔
3980
                {
3981
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
3982
                             "Radius value should be strictly positive when "
3983
                             "per quadrant parameters are specified");
3984
                    CSLDestroy(papszParams);
×
3985
                    return CE_Failure;
×
3986
                }
3987
                if (poAverageOpts->dfAngle != 0)
9✔
3988
                {
3989
                    CPLError(CE_Failure, CPLE_NotSupported,
×
3990
                             "angle != 0 not supported when "
3991
                             "per quadrant parameters are specified");
3992
                    CSLDestroy(papszParams);
×
3993
                    return CE_Failure;
×
3994
                }
3995
            }
3996
            else if (poAverageOpts->nMaxPoints > 0)
17✔
3997
            {
3998
                CPLError(CE_Warning, CPLE_AppDefined,
×
3999
                         "max_points is ignored unless one of "
4000
                         "min_points_per_quadrant or max_points_per_quadrant "
4001
                         "is >= 1");
4002
            }
4003

4004
            static const char *const apszKnownOptions[] = {
4005
                "radius",
4006
                "radius1",
4007
                "radius2",
4008
                "angle",
4009
                "min_points",
4010
                "max_points",
4011
                "nodata",
4012
                "min_points_per_quadrant",
4013
                "max_points_per_quadrant",
4014
                nullptr};
4015
            papszKnownOptions = apszKnownOptions;
26✔
4016

4017
            break;
26✔
4018
        }
4019
        case GGA_NearestNeighbor:
19✔
4020
        {
4021
            *ppOptions = CPLMalloc(sizeof(GDALGridNearestNeighborOptions));
19✔
4022

4023
            GDALGridNearestNeighborOptions *const poNeighborOpts =
19✔
4024
                static_cast<GDALGridNearestNeighborOptions *>(*ppOptions);
4025

4026
            poNeighborOpts->nSizeOfStructure = sizeof(*poNeighborOpts);
19✔
4027

4028
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
19✔
4029
            if (pszValue)
19✔
4030
            {
4031
                poNeighborOpts->dfRadius1 = CPLAtofM(pszValue);
2✔
4032
                poNeighborOpts->dfRadius2 = poNeighborOpts->dfRadius1;
2✔
4033
            }
4034
            else
4035
            {
4036
                pszValue = CSLFetchNameValue(papszParams, "radius1");
17✔
4037
                poNeighborOpts->dfRadius1 = pszValue ? CPLAtofM(pszValue) : 0.0;
17✔
4038

4039
                pszValue = CSLFetchNameValue(papszParams, "radius2");
17✔
4040
                poNeighborOpts->dfRadius2 = pszValue ? CPLAtofM(pszValue) : 0.0;
17✔
4041
            }
4042

4043
            pszValue = CSLFetchNameValue(papszParams, "angle");
19✔
4044
            poNeighborOpts->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
19✔
4045

4046
            pszValue = CSLFetchNameValue(papszParams, "nodata");
19✔
4047
            poNeighborOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
19✔
4048

4049
            static const char *const apszKnownOptions[] = {
4050
                "radius", "radius1", "radius2", "angle", "nodata", nullptr};
4051
            papszKnownOptions = apszKnownOptions;
19✔
4052

4053
            break;
19✔
4054
        }
4055
        case GGA_MetricMinimum:
63✔
4056
        case GGA_MetricMaximum:
4057
        case GGA_MetricRange:
4058
        case GGA_MetricCount:
4059
        case GGA_MetricAverageDistance:
4060
        case GGA_MetricAverageDistancePts:
4061
        {
4062
            *ppOptions = CPLMalloc(sizeof(GDALGridDataMetricsOptions));
63✔
4063

4064
            GDALGridDataMetricsOptions *const poMetricsOptions =
63✔
4065
                static_cast<GDALGridDataMetricsOptions *>(*ppOptions);
4066

4067
            poMetricsOptions->nSizeOfStructure = sizeof(*poMetricsOptions);
63✔
4068

4069
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
63✔
4070
            if (pszValue)
63✔
4071
            {
4072
                poMetricsOptions->dfRadius1 = CPLAtofM(pszValue);
31✔
4073
                poMetricsOptions->dfRadius2 = poMetricsOptions->dfRadius1;
31✔
4074
            }
4075
            else
4076
            {
4077
                pszValue = CSLFetchNameValue(papszParams, "radius1");
32✔
4078
                poMetricsOptions->dfRadius1 =
32✔
4079
                    pszValue ? CPLAtofM(pszValue) : 0.0;
32✔
4080

4081
                pszValue = CSLFetchNameValue(papszParams, "radius2");
32✔
4082
                poMetricsOptions->dfRadius2 =
32✔
4083
                    pszValue ? CPLAtofM(pszValue) : 0.0;
32✔
4084
            }
4085

4086
            pszValue = CSLFetchNameValue(papszParams, "angle");
63✔
4087
            poMetricsOptions->dfAngle = pszValue ? CPLAtofM(pszValue) : 0.0;
63✔
4088

4089
            pszValue = CSLFetchNameValue(papszParams, "min_points");
63✔
4090
            poMetricsOptions->nMinPoints = pszValue ? atoi(pszValue) : 0;
63✔
4091

4092
            pszValue = CSLFetchNameValue(papszParams, "nodata");
63✔
4093
            poMetricsOptions->dfNoDataValue =
63✔
4094
                pszValue ? CPLAtofM(pszValue) : 0.0;
63✔
4095

4096
            pszValue =
4097
                CSLFetchNameValue(papszParams, "min_points_per_quadrant");
63✔
4098
            poMetricsOptions->nMinPointsPerQuadrant =
63✔
4099
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
63✔
4100

4101
            pszValue =
4102
                CSLFetchNameValue(papszParams, "max_points_per_quadrant");
63✔
4103
            poMetricsOptions->nMaxPointsPerQuadrant =
63✔
4104
                static_cast<GUInt32>(pszValue ? CPLAtofM(pszValue) : 0);
63✔
4105

4106
            if (poMetricsOptions->nMinPointsPerQuadrant != 0 ||
63✔
4107
                poMetricsOptions->nMaxPointsPerQuadrant != 0)
37✔
4108
            {
4109
                if (*peAlgorithm == GGA_MetricAverageDistancePts)
27✔
4110
                {
4111
                    CPLError(CE_Failure, CPLE_NotSupported,
×
4112
                             "Algorithm %s not supported when "
4113
                             "per quadrant parameters are specified",
4114
                             szAlgNameAverageDistancePts);
4115
                    CSLDestroy(papszParams);
×
4116
                    return CE_Failure;
×
4117
                }
4118
                if (!(poMetricsOptions->dfRadius1 > 0) ||
27✔
4119
                    !(poMetricsOptions->dfRadius2 > 0))
27✔
4120
                {
4121
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
4122
                             "Radius value should be strictly positive when "
4123
                             "per quadrant parameters are specified");
4124
                    CSLDestroy(papszParams);
×
4125
                    return CE_Failure;
×
4126
                }
4127
                if (poMetricsOptions->dfAngle != 0)
27✔
4128
                {
4129
                    CPLError(CE_Failure, CPLE_NotSupported,
×
4130
                             "angle != 0 not supported when "
4131
                             "per quadrant parameters are specified");
4132
                    CSLDestroy(papszParams);
×
4133
                    return CE_Failure;
×
4134
                }
4135
            }
4136

4137
            static const char *const apszKnownOptions[] = {
4138
                "radius",
4139
                "radius1",
4140
                "radius2",
4141
                "angle",
4142
                "min_points",
4143
                "nodata",
4144
                "min_points_per_quadrant",
4145
                "max_points_per_quadrant",
4146
                nullptr};
4147
            papszKnownOptions = apszKnownOptions;
63✔
4148

4149
            break;
63✔
4150
        }
4151
        case GGA_Linear:
7✔
4152
        {
4153
            *ppOptions = CPLMalloc(sizeof(GDALGridLinearOptions));
7✔
4154

4155
            GDALGridLinearOptions *const poLinearOpts =
7✔
4156
                static_cast<GDALGridLinearOptions *>(*ppOptions);
4157

4158
            poLinearOpts->nSizeOfStructure = sizeof(*poLinearOpts);
7✔
4159

4160
            const char *pszValue = CSLFetchNameValue(papszParams, "radius");
7✔
4161
            poLinearOpts->dfRadius = pszValue ? CPLAtofM(pszValue) : -1.0;
7✔
4162

4163
            pszValue = CSLFetchNameValue(papszParams, "nodata");
7✔
4164
            poLinearOpts->dfNoDataValue = pszValue ? CPLAtofM(pszValue) : 0.0;
7✔
4165

4166
            static const char *const apszKnownOptions[] = {"radius", "nodata",
4167
                                                           nullptr};
4168
            papszKnownOptions = apszKnownOptions;
7✔
4169

4170
            break;
7✔
4171
        }
4172
    }
4173

4174
    if (papszKnownOptions)
382✔
4175
    {
4176
        for (int i = 1; papszParams[i] != nullptr; ++i)
1,084✔
4177
        {
4178
            char *pszKey = nullptr;
702✔
4179
            CPL_IGNORE_RET_VAL(CPLParseNameValue(papszParams[i], &pszKey));
702✔
4180
            if (pszKey)
702✔
4181
            {
4182
                bool bKnownKey = false;
702✔
4183
                for (const char *const *papszKnownKeyIter = papszKnownOptions;
2,992✔
4184
                     *papszKnownKeyIter; ++papszKnownKeyIter)
2,992✔
4185
                {
4186
                    if (EQUAL(*papszKnownKeyIter, pszKey))
2,992✔
4187
                    {
4188
                        bKnownKey = true;
702✔
4189
                        break;
702✔
4190
                    }
4191
                }
4192
                if (!bKnownKey)
702✔
4193
                {
4194
                    CPLError(CE_Warning, CPLE_AppDefined, "Option %s ignored",
×
4195
                             pszKey);
4196
                }
4197
            }
4198
            CPLFree(pszKey);
702✔
4199
        }
4200
    }
4201

4202
    CSLDestroy(papszParams);
382✔
4203
    return CE_None;
382✔
4204
}
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