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