Coverage Report

Created: 2025-06-13 06:18

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