Coverage Report

Created: 2026-04-01 06:20

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