Coverage Report

Created: 2026-02-14 06:52

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