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

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

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

80.84
/apps/gdal_grid_lib.cpp
1
/* ****************************************************************************
2
 *
3
 * Project:  GDAL Utilities
4
 * Purpose:  GDAL scattered data gridding (interpolation) tool
5
 * Author:   Andrey Kiselev, dron@ak4719.spb.edu
6
 *
7
 * ****************************************************************************
8
 * Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
9
 * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "gdal_utils.h"
16
#include "gdal_utils_priv.h"
17
#include "commonutils.h"
18
#include "gdalargumentparser.h"
19

20
#include <cmath>
21
#include <cstdint>
22
#include <cstdio>
23
#include <cstdlib>
24
#include <algorithm>
25
#include <vector>
26

27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_progress.h"
30
#include "cpl_string.h"
31
#include "cpl_vsi.h"
32
#include "gdal.h"
33
#include "gdal_alg.h"
34
#include "gdal_priv.h"
35
#include "gdalgrid.h"
36
#include "ogr_api.h"
37
#include "ogr_core.h"
38
#include "ogr_feature.h"
39
#include "ogr_geometry.h"
40
#include "ogr_spatialref.h"
41
#include "ogr_srs_api.h"
42
#include "ogrsf_frmts.h"
43

44
/************************************************************************/
45
/*                          GDALGridOptions                             */
46
/************************************************************************/
47

48
/** Options for use with GDALGrid(). GDALGridOptions* must be allocated
49
 * and freed with GDALGridOptionsNew() and GDALGridOptionsFree() respectively.
50
 */
51
struct GDALGridOptions
52
{
53
    /*! output format. Use the short format name. */
54
    std::string osFormat{};
55

56
    /*! allow or suppress progress monitor and other non-error output */
57
    bool bQuiet = true;
58

59
    /*! the progress function to use */
60
    GDALProgressFunc pfnProgress = GDALDummyProgress;
61

62
    /*! pointer to the progress data variable */
63
    void *pProgressData = nullptr;
64

65
    CPLStringList aosLayers{};
66
    std::string osBurnAttribute{};
67
    double dfIncreaseBurnValue = 0.0;
68
    double dfMultiplyBurnValue = 1.0;
69
    std::string osWHERE{};
70
    std::string osSQL{};
71
    GDALDataType eOutputType = GDT_Float64;
72
    CPLStringList aosCreateOptions{};
73
    int nXSize = 0;
74
    int nYSize = 0;
75
    double dfXRes = 0;
76
    double dfYRes = 0;
77
    double dfXMin = 0;
78
    double dfXMax = 0;
79
    double dfYMin = 0;
80
    double dfYMax = 0;
81
    bool bIsXExtentSet = false;
82
    bool bIsYExtentSet = false;
83
    GDALGridAlgorithm eAlgorithm = GGA_InverseDistanceToAPower;
84
    std::unique_ptr<void, VSIFreeReleaser> pOptions{};
85
    std::string osOutputSRS{};
86
    std::unique_ptr<OGRGeometry> poSpatialFilter{};
87
    bool bClipSrc = false;
88
    std::unique_ptr<OGRGeometry> poClipSrc{};
89
    std::string osClipSrcDS{};
90
    std::string osClipSrcSQL{};
91
    std::string osClipSrcLayer{};
92
    std::string osClipSrcWhere{};
93
    bool bNoDataSet = false;
94
    double dfNoDataValue = 0;
95

96
    GDALGridOptions()
183✔
97
    {
183✔
98
        void *l_pOptions = nullptr;
183✔
99
        GDALGridParseAlgorithmAndOptions(szAlgNameInvDist, &eAlgorithm,
183✔
100
                                         &l_pOptions);
101
        pOptions.reset(l_pOptions);
183✔
102
    }
183✔
103

104
    CPL_DISALLOW_COPY_ASSIGN(GDALGridOptions)
105
};
106

107
/************************************************************************/
108
/*                          GetAlgorithmName()                          */
109
/*                                                                      */
110
/*      Grids algorithm code into mnemonic name.                        */
111
/************************************************************************/
112

113
static void PrintAlgorithmAndOptions(GDALGridAlgorithm eAlgorithm,
54✔
114
                                     void *pOptions)
115
{
116
    switch (eAlgorithm)
54✔
117
    {
118
        case GGA_InverseDistanceToAPower:
6✔
119
        {
120
            printf("Algorithm name: \"%s\".\n", szAlgNameInvDist);
6✔
121
            GDALGridInverseDistanceToAPowerOptions *pOptions2 =
6✔
122
                static_cast<GDALGridInverseDistanceToAPowerOptions *>(pOptions);
123
            CPLprintf("Options are "
6✔
124
                      "\"power=%f:smoothing=%f:radius1=%f:radius2=%f:angle=%f"
125
                      ":max_points=%u:min_points=%u:nodata=%f\"\n",
126
                      pOptions2->dfPower, pOptions2->dfSmoothing,
127
                      pOptions2->dfRadius1, pOptions2->dfRadius2,
128
                      pOptions2->dfAngle, pOptions2->nMaxPoints,
129
                      pOptions2->nMinPoints, pOptions2->dfNoDataValue);
130
            break;
6✔
131
        }
132
        case GGA_InverseDistanceToAPowerNearestNeighbor:
3✔
133
        {
134
            printf("Algorithm name: \"%s\".\n",
3✔
135
                   szAlgNameInvDistNearestNeighbor);
136
            GDALGridInverseDistanceToAPowerNearestNeighborOptions *pOptions2 =
3✔
137
                static_cast<
138
                    GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
139
                    pOptions);
140
            CPLString osStr;
6✔
141
            osStr.Printf("power=%f:smoothing=%f:radius=%f"
142
                         ":max_points=%u:min_points=%u:nodata=%f",
143
                         pOptions2->dfPower, pOptions2->dfSmoothing,
144
                         pOptions2->dfRadius, pOptions2->nMaxPoints,
145
                         pOptions2->nMinPoints, pOptions2->dfNoDataValue);
3✔
146
            if (pOptions2->nMinPointsPerQuadrant > 0)
3✔
147
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
148
                                    pOptions2->nMinPointsPerQuadrant);
×
149
            if (pOptions2->nMaxPointsPerQuadrant > 0)
3✔
150
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
151
                                    pOptions2->nMaxPointsPerQuadrant);
×
152
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
3✔
153
            break;
3✔
154
        }
155
        case GGA_MovingAverage:
6✔
156
        {
157
            printf("Algorithm name: \"%s\".\n", szAlgNameAverage);
6✔
158
            GDALGridMovingAverageOptions *pOptions2 =
6✔
159
                static_cast<GDALGridMovingAverageOptions *>(pOptions);
160
            CPLString osStr;
12✔
161
            osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
162
                         ":nodata=%f",
163
                         pOptions2->dfRadius1, pOptions2->dfRadius2,
164
                         pOptions2->dfAngle, pOptions2->nMinPoints,
165
                         pOptions2->dfNoDataValue);
6✔
166
            if (pOptions2->nMinPointsPerQuadrant > 0)
6✔
167
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
168
                                    pOptions2->nMinPointsPerQuadrant);
×
169
            if (pOptions2->nMaxPointsPerQuadrant > 0)
6✔
170
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
171
                                    pOptions2->nMaxPointsPerQuadrant);
×
172
            if (pOptions2->nMaxPoints > 0)
6✔
173
                osStr += CPLSPrintf(":max_points=%u", pOptions2->nMaxPoints);
×
174
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
6✔
175
            break;
6✔
176
        }
177
        case GGA_NearestNeighbor:
12✔
178
        {
179
            printf("Algorithm name: \"%s\".\n", szAlgNameNearest);
12✔
180
            GDALGridNearestNeighborOptions *pOptions2 =
12✔
181
                static_cast<GDALGridNearestNeighborOptions *>(pOptions);
182
            CPLprintf("Options are "
12✔
183
                      "\"radius1=%f:radius2=%f:angle=%f:nodata=%f\"\n",
184
                      pOptions2->dfRadius1, pOptions2->dfRadius2,
185
                      pOptions2->dfAngle, pOptions2->dfNoDataValue);
186
            break;
12✔
187
        }
188
        case GGA_MetricMinimum:
26✔
189
        case GGA_MetricMaximum:
190
        case GGA_MetricRange:
191
        case GGA_MetricCount:
192
        case GGA_MetricAverageDistance:
193
        case GGA_MetricAverageDistancePts:
194
        {
195
            const char *pszAlgName = "";
26✔
196
            CPL_IGNORE_RET_VAL(pszAlgName);  // Make CSA happy
26✔
197
            switch (eAlgorithm)
26✔
198
            {
199
                case GGA_MetricMinimum:
6✔
200
                    pszAlgName = szAlgNameMinimum;
6✔
201
                    break;
6✔
202
                case GGA_MetricMaximum:
6✔
203
                    pszAlgName = szAlgNameMaximum;
6✔
204
                    break;
6✔
205
                case GGA_MetricRange:
3✔
206
                    pszAlgName = szAlgNameRange;
3✔
207
                    break;
3✔
208
                case GGA_MetricCount:
5✔
209
                    pszAlgName = szAlgNameCount;
5✔
210
                    break;
5✔
211
                case GGA_MetricAverageDistance:
3✔
212
                    pszAlgName = szAlgNameAverageDistance;
3✔
213
                    break;
3✔
214
                case GGA_MetricAverageDistancePts:
3✔
215
                    pszAlgName = szAlgNameAverageDistancePts;
3✔
216
                    break;
3✔
217
                default:
×
218
                    CPLAssert(false);
×
219
                    break;
220
            }
221
            printf("Algorithm name: \"%s\".\n", pszAlgName);
26✔
222
            GDALGridDataMetricsOptions *pOptions2 =
26✔
223
                static_cast<GDALGridDataMetricsOptions *>(pOptions);
224
            CPLString osStr;
52✔
225
            osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
226
                         ":nodata=%f",
227
                         pOptions2->dfRadius1, pOptions2->dfRadius2,
228
                         pOptions2->dfAngle, pOptions2->nMinPoints,
229
                         pOptions2->dfNoDataValue);
26✔
230
            if (pOptions2->nMinPointsPerQuadrant > 0)
26✔
231
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
232
                                    pOptions2->nMinPointsPerQuadrant);
×
233
            if (pOptions2->nMaxPointsPerQuadrant > 0)
26✔
234
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
235
                                    pOptions2->nMaxPointsPerQuadrant);
×
236
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
26✔
237
            break;
26✔
238
        }
239
        case GGA_Linear:
1✔
240
        {
241
            printf("Algorithm name: \"%s\".\n", szAlgNameLinear);
1✔
242
            GDALGridLinearOptions *pOptions2 =
1✔
243
                static_cast<GDALGridLinearOptions *>(pOptions);
244
            CPLprintf("Options are "
1✔
245
                      "\"radius=%f:nodata=%f\"\n",
246
                      pOptions2->dfRadius, pOptions2->dfNoDataValue);
247
            break;
1✔
248
        }
249
        default:
×
250
        {
251
            printf("Algorithm is unknown.\n");
×
252
            break;
×
253
        }
254
    }
255
}
54✔
256

257
/************************************************************************/
258
/*  Extract point coordinates from the geometry reference and set the   */
259
/*  Z value as requested. Test whether we are in the clipped region     */
260
/*  before processing.                                                  */
261
/************************************************************************/
262

263
class GDALGridGeometryVisitor final : public OGRDefaultConstGeometryVisitor
264
{
265
  public:
266
    const OGRGeometry *poClipSrc = nullptr;
267
    int iBurnField = 0;
268
    double dfBurnValue = 0;
269
    double dfIncreaseBurnValue = 0;
270
    double dfMultiplyBurnValue = 1;
271
    std::vector<double> adfX{};
272
    std::vector<double> adfY{};
273
    std::vector<double> adfZ{};
274

275
    using OGRDefaultConstGeometryVisitor::visit;
276

277
    void visit(const OGRPoint *p) override;
278
};
279

280
void GDALGridGeometryVisitor::visit(const OGRPoint *p)
65,300✔
281
{
282
    if (poClipSrc && !p->Within(poClipSrc))
65,300✔
283
        return;
20✔
284

285
    if (iBurnField < 0 && std::isnan(p->getZ()))
65,280✔
286
        return;
1✔
287

288
    adfX.push_back(p->getX());
65,279✔
289
    adfY.push_back(p->getY());
65,279✔
290
    if (iBurnField < 0)
65,279✔
291
        adfZ.push_back((p->getZ() + dfIncreaseBurnValue) * dfMultiplyBurnValue);
65,270✔
292
    else
293
        adfZ.push_back((dfBurnValue + dfIncreaseBurnValue) *
9✔
294
                       dfMultiplyBurnValue);
9✔
295
}
296

297
/************************************************************************/
298
/*                            ProcessLayer()                            */
299
/*                                                                      */
300
/*      Process all the features in a layer selection, collecting       */
301
/*      geometries and burn values.                                     */
302
/************************************************************************/
303

304
static CPLErr ProcessLayer(OGRLayer *poSrcLayer, GDALDataset *poDstDS,
178✔
305
                           const OGRGeometry *poClipSrc, int nXSize, int nYSize,
306
                           int nBand, bool &bIsXExtentSet, bool &bIsYExtentSet,
307
                           double &dfXMin, double &dfXMax, double &dfYMin,
308
                           double &dfYMax, const std::string &osBurnAttribute,
309
                           const double dfIncreaseBurnValue,
310
                           const double dfMultiplyBurnValue, GDALDataType eType,
311
                           GDALGridAlgorithm eAlgorithm, void *pOptions,
312
                           bool bQuiet, GDALProgressFunc pfnProgress,
313
                           void *pProgressData)
314

315
{
316
    /* -------------------------------------------------------------------- */
317
    /*      Get field index, and check.                                     */
318
    /* -------------------------------------------------------------------- */
319
    int iBurnField = -1;
178✔
320

321
    if (!osBurnAttribute.empty())
178✔
322
    {
323
        iBurnField =
324
            poSrcLayer->GetLayerDefn()->GetFieldIndex(osBurnAttribute.c_str());
2✔
325
        if (iBurnField == -1)
2✔
326
        {
327
            printf("Failed to find field %s on layer %s, skipping.\n",
×
328
                   osBurnAttribute.c_str(), poSrcLayer->GetName());
×
329
            return CE_Failure;
×
330
        }
331
    }
332

333
    /* -------------------------------------------------------------------- */
334
    /*      Collect the geometries from this layer, and build list of       */
335
    /*      values to be interpolated.                                      */
336
    /* -------------------------------------------------------------------- */
337
    GDALGridGeometryVisitor oVisitor;
356✔
338
    oVisitor.poClipSrc = poClipSrc;
178✔
339
    oVisitor.iBurnField = iBurnField;
178✔
340
    oVisitor.dfIncreaseBurnValue = dfIncreaseBurnValue;
178✔
341
    oVisitor.dfMultiplyBurnValue = dfMultiplyBurnValue;
178✔
342

343
    for (auto &&poFeat : poSrcLayer)
65,329✔
344
    {
345
        const OGRGeometry *poGeom = poFeat->GetGeometryRef();
65,151✔
346
        if (poGeom)
65,151✔
347
        {
348
            if (iBurnField >= 0)
65,151✔
349
            {
350
                if (!poFeat->IsFieldSetAndNotNull(iBurnField))
10✔
351
                {
352
                    continue;
1✔
353
                }
354
                oVisitor.dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
9✔
355
            }
356

357
            poGeom->accept(&oVisitor);
65,150✔
358
        }
359
    }
360

361
    if (oVisitor.adfX.empty())
178✔
362
    {
363
        printf("No point geometry found on layer %s, skipping.\n",
×
364
               poSrcLayer->GetName());
×
365
        return CE_None;
×
366
    }
367

368
    /* -------------------------------------------------------------------- */
369
    /*      Compute grid geometry.                                          */
370
    /* -------------------------------------------------------------------- */
371
    if (!bIsXExtentSet || !bIsYExtentSet)
178✔
372
    {
373
        OGREnvelope sEnvelope;
69✔
374
        if (poSrcLayer->GetExtent(&sEnvelope, TRUE) == OGRERR_FAILURE)
69✔
375
        {
376
            return CE_Failure;
×
377
        }
378

379
        if (!bIsXExtentSet)
69✔
380
        {
381
            dfXMin = sEnvelope.MinX;
69✔
382
            dfXMax = sEnvelope.MaxX;
69✔
383
            bIsXExtentSet = true;
69✔
384
        }
385

386
        if (!bIsYExtentSet)
69✔
387
        {
388
            dfYMin = sEnvelope.MinY;
69✔
389
            dfYMax = sEnvelope.MaxY;
69✔
390
            bIsYExtentSet = true;
69✔
391
        }
392
    }
393

394
    // Produce north-up images
395
    if (dfYMin < dfYMax)
178✔
396
        std::swap(dfYMin, dfYMax);
126✔
397

398
    /* -------------------------------------------------------------------- */
399
    /*      Perform gridding.                                               */
400
    /* -------------------------------------------------------------------- */
401

402
    const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
178✔
403
    const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
178✔
404

405
    if (!bQuiet)
178✔
406
    {
407
        printf("Grid data type is \"%s\"\n", GDALGetDataTypeName(eType));
54✔
408
        printf("Grid size = (%d %d).\n", nXSize, nYSize);
54✔
409
        CPLprintf("Corner coordinates = (%f %f)-(%f %f).\n", dfXMin, dfYMin,
54✔
410
                  dfXMax, dfYMax);
411
        CPLprintf("Grid cell size = (%f %f).\n", dfDeltaX, dfDeltaY);
54✔
412
        printf("Source point count = %lu.\n",
54✔
413
               static_cast<unsigned long>(oVisitor.adfX.size()));
54✔
414
        PrintAlgorithmAndOptions(eAlgorithm, pOptions);
54✔
415
        printf("\n");
54✔
416
    }
417

418
    GDALRasterBand *poBand = poDstDS->GetRasterBand(nBand);
178✔
419

420
    int nBlockXSize = 0;
178✔
421
    int nBlockYSize = 0;
178✔
422
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
178✔
423

424
    // Try to grow the work buffer up to 16 MB if it is smaller
425
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
178✔
426
    if (nXSize == 0 || nYSize == 0 || nBlockXSize == 0 || nBlockYSize == 0)
178✔
427
        return CE_Failure;
×
428

429
    const int nDesiredBufferSize = 16 * 1024 * 1024;
178✔
430
    if (nBlockXSize < nXSize && nBlockYSize < nYSize &&
178✔
431
        nBlockXSize < nDesiredBufferSize / (nBlockYSize * nDataTypeSize))
×
432
    {
433
        const int nNewBlockXSize =
×
434
            nDesiredBufferSize / (nBlockYSize * nDataTypeSize);
×
435
        nBlockXSize = (nNewBlockXSize / nBlockXSize) * nBlockXSize;
×
436
        if (nBlockXSize > nXSize)
×
437
            nBlockXSize = nXSize;
×
438
    }
439
    else if (nBlockXSize == nXSize && nBlockYSize < nYSize &&
178✔
440
             nBlockYSize < nDesiredBufferSize / (nXSize * nDataTypeSize))
81✔
441
    {
442
        const int nNewBlockYSize =
81✔
443
            nDesiredBufferSize / (nXSize * nDataTypeSize);
81✔
444
        nBlockYSize = (nNewBlockYSize / nBlockYSize) * nBlockYSize;
81✔
445
        if (nBlockYSize > nYSize)
81✔
446
            nBlockYSize = nYSize;
81✔
447
    }
448
    CPLDebug("GDAL_GRID", "Work buffer: %d * %d", nBlockXSize, nBlockYSize);
178✔
449

450
    std::unique_ptr<void, VSIFreeReleaser> pData(
451
        VSIMalloc3(nBlockXSize, nBlockYSize, nDataTypeSize));
356✔
452
    if (!pData)
178✔
453
    {
454
        CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate work buffer");
×
455
        return CE_Failure;
×
456
    }
457

458
    GIntBig nBlock = 0;
178✔
459
    const double dfBlockCount =
178✔
460
        static_cast<double>(DIV_ROUND_UP(nXSize, nBlockXSize)) *
178✔
461
        DIV_ROUND_UP(nYSize, nBlockYSize);
178✔
462

463
    struct GDALGridContextReleaser
464
    {
465
        void operator()(GDALGridContext *psContext)
178✔
466
        {
467
            GDALGridContextFree(psContext);
178✔
468
        }
178✔
469
    };
470

471
    std::unique_ptr<GDALGridContext, GDALGridContextReleaser> psContext(
472
        GDALGridContextCreate(eAlgorithm, pOptions,
473
                              static_cast<int>(oVisitor.adfX.size()),
178✔
474
                              &(oVisitor.adfX[0]), &(oVisitor.adfY[0]),
178✔
475
                              &(oVisitor.adfZ[0]), TRUE));
534✔
476
    if (!psContext)
178✔
477
    {
478
        return CE_Failure;
×
479
    }
480

481
    CPLErr eErr = CE_None;
178✔
482
    for (int nYOffset = 0; nYOffset < nYSize && eErr == CE_None;
356✔
483
         nYOffset += nBlockYSize)
178✔
484
    {
485
        for (int nXOffset = 0; nXOffset < nXSize && eErr == CE_None;
356✔
486
             nXOffset += nBlockXSize)
178✔
487
        {
488
            std::unique_ptr<void, GDALScaledProgressReleaser> pScaledProgress(
489
                GDALCreateScaledProgress(
490
                    static_cast<double>(nBlock) / dfBlockCount,
178✔
491
                    static_cast<double>(nBlock + 1) / dfBlockCount, pfnProgress,
178✔
492
                    pProgressData));
356✔
493
            nBlock++;
178✔
494

495
            int nXRequest = nBlockXSize;
178✔
496
            if (nXOffset > nXSize - nXRequest)
178✔
497
                nXRequest = nXSize - nXOffset;
2✔
498

499
            int nYRequest = nBlockYSize;
178✔
500
            if (nYOffset > nYSize - nYRequest)
178✔
501
                nYRequest = nYSize - nYOffset;
2✔
502

503
            eErr = GDALGridContextProcess(
356✔
504
                psContext.get(), dfXMin + dfDeltaX * nXOffset,
178✔
505
                dfXMin + dfDeltaX * (nXOffset + nXRequest),
178✔
506
                dfYMin + dfDeltaY * nYOffset,
178✔
507
                dfYMin + dfDeltaY * (nYOffset + nYRequest), nXRequest,
178✔
508
                nYRequest, eType, pData.get(), GDALScaledProgress,
509
                pScaledProgress.get());
510

511
            if (eErr == CE_None)
178✔
512
                eErr = poBand->RasterIO(GF_Write, nXOffset, nYOffset, nXRequest,
178✔
513
                                        nYRequest, pData.get(), nXRequest,
514
                                        nYRequest, eType, 0, 0, nullptr);
515
        }
516
    }
517
    if (eErr == CE_None && pfnProgress)
178✔
518
        pfnProgress(1.0, "", pProgressData);
104✔
519

520
    return eErr;
178✔
521
}
522

523
/************************************************************************/
524
/*                            LoadGeometry()                            */
525
/*                                                                      */
526
/*  Read geometries from the given dataset using specified filters and  */
527
/*  returns a collection of read geometries.                            */
528
/************************************************************************/
529

530
static std::unique_ptr<OGRGeometry> LoadGeometry(const std::string &osDS,
1✔
531
                                                 const std::string &osSQL,
532
                                                 const std::string &osLyr,
533
                                                 const std::string &osWhere)
534
{
535
    auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
536
        osDS.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
2✔
537
    if (!poDS)
1✔
538
        return nullptr;
×
539

540
    OGRLayer *poLyr = nullptr;
1✔
541
    if (!osSQL.empty())
1✔
542
        poLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
×
543
    else if (!osLyr.empty())
1✔
544
        poLyr = poDS->GetLayerByName(osLyr.c_str());
×
545
    else
546
        poLyr = poDS->GetLayer(0);
1✔
547

548
    if (poLyr == nullptr)
1✔
549
    {
550
        CPLError(CE_Failure, CPLE_AppDefined,
×
551
                 "Failed to identify source layer from datasource.");
552
        return nullptr;
×
553
    }
554

555
    if (!osWhere.empty())
1✔
556
        poLyr->SetAttributeFilter(osWhere.c_str());
×
557

558
    std::unique_ptr<OGRGeometryCollection> poGeom;
1✔
559
    for (auto &poFeat : poLyr)
2✔
560
    {
561
        const OGRGeometry *poSrcGeom = poFeat->GetGeometryRef();
1✔
562
        if (poSrcGeom)
1✔
563
        {
564
            const OGRwkbGeometryType eType =
565
                wkbFlatten(poSrcGeom->getGeometryType());
1✔
566

567
            if (!poGeom)
1✔
568
                poGeom = std::make_unique<OGRMultiPolygon>();
1✔
569

570
            if (eType == wkbPolygon)
1✔
571
            {
572
                poGeom->addGeometry(poSrcGeom);
1✔
573
            }
574
            else if (eType == wkbMultiPolygon)
×
575
            {
576
                const int nGeomCount =
577
                    poSrcGeom->toMultiPolygon()->getNumGeometries();
×
578

579
                for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
×
580
                {
581
                    poGeom->addGeometry(
×
582
                        poSrcGeom->toMultiPolygon()->getGeometryRef(iGeom));
×
583
                }
584
            }
585
            else
586
            {
587
                CPLError(CE_Failure, CPLE_AppDefined,
×
588
                         "Geometry not of polygon type.");
589
                if (!osSQL.empty())
×
590
                    poDS->ReleaseResultSet(poLyr);
×
591
                return nullptr;
×
592
            }
593
        }
594
    }
595

596
    if (!osSQL.empty())
1✔
597
        poDS->ReleaseResultSet(poLyr);
×
598

599
    return poGeom;
1✔
600
}
601

602
/************************************************************************/
603
/*                               GDALGrid()                             */
604
/************************************************************************/
605

606
/* clang-format off */
607
/**
608
 * Create raster from the scattered data.
609
 *
610
 * This is the equivalent of the
611
 * <a href="/programs/gdal_grid.html">gdal_grid</a> utility.
612
 *
613
 * GDALGridOptions* must be allocated and freed with GDALGridOptionsNew()
614
 * and GDALGridOptionsFree() respectively.
615
 *
616
 * @param pszDest the destination dataset path.
617
 * @param hSrcDataset the source dataset handle.
618
 * @param psOptionsIn the options struct returned by GDALGridOptionsNew() or
619
 * NULL.
620
 * @param pbUsageError pointer to a integer output variable to store if any
621
 * usage error has occurred or NULL.
622
 * @return the output dataset (new dataset that must be closed using
623
 * GDALClose()) or NULL in case of error.
624
 *
625
 * @since GDAL 2.1
626
 */
627
/* clang-format on */
628

629
GDALDatasetH GDALGrid(const char *pszDest, GDALDatasetH hSrcDataset,
183✔
630
                      const GDALGridOptions *psOptionsIn, int *pbUsageError)
631

632
{
633
    if (hSrcDataset == nullptr)
183✔
634
    {
635
        CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
×
636

637
        if (pbUsageError)
×
638
            *pbUsageError = TRUE;
×
639
        return nullptr;
×
640
    }
641
    if (pszDest == nullptr)
183✔
642
    {
643
        CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
×
644

645
        if (pbUsageError)
×
646
            *pbUsageError = TRUE;
×
647
        return nullptr;
×
648
    }
649

650
    std::unique_ptr<GDALGridOptions> psOptionsToFree;
183✔
651
    const GDALGridOptions *psOptions = psOptionsIn;
183✔
652
    if (psOptions == nullptr)
183✔
653
    {
654
        psOptionsToFree = std::make_unique<GDALGridOptions>();
×
655
        psOptions = psOptionsToFree.get();
×
656
    }
657

658
    GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
183✔
659

660
    if (psOptions->osSQL.empty() && psOptions->aosLayers.empty() &&
302✔
661
        poSrcDS->GetLayerCount() != 1)
119✔
662
    {
663
        CPLError(CE_Failure, CPLE_NotSupported,
×
664
                 "Neither -sql nor -l are specified, but the source dataset "
665
                 "has not one single layer.");
666
        if (pbUsageError)
×
667
            *pbUsageError = TRUE;
×
668
        return nullptr;
×
669
    }
670

671
    if ((psOptions->nXSize != 0 || psOptions->nYSize != 0) &&
183✔
672
        (psOptions->dfXRes != 0 || psOptions->dfYRes != 0))
106✔
673
    {
674
        CPLError(CE_Failure, CPLE_IllegalArg,
×
675
                 "-outsize and -tr options cannot be used at the same time.");
676
        return nullptr;
×
677
    }
678

679
    /* -------------------------------------------------------------------- */
680
    /*      Find the output driver.                                         */
681
    /* -------------------------------------------------------------------- */
682
    std::string osFormat;
366✔
683
    if (psOptions->osFormat.empty())
183✔
684
    {
685
        osFormat = GetOutputDriverForRaster(pszDest);
58✔
686
        if (osFormat.empty())
58✔
687
        {
688
            return nullptr;
×
689
        }
690
    }
691
    else
692
    {
693
        osFormat = psOptions->osFormat;
125✔
694
    }
695

696
    GDALDriverH hDriver = GDALGetDriverByName(osFormat.c_str());
183✔
697
    if (hDriver == nullptr)
183✔
698
    {
699
        CPLError(CE_Failure, CPLE_AppDefined,
×
700
                 "Output driver `%s' not recognised.", osFormat.c_str());
701
        fprintf(stderr, "The following format drivers are enabled and "
×
702
                        "support writing:\n");
703
        for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
×
704
        {
705
            hDriver = GDALGetDriver(iDr);
×
706

707
            if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
×
708
                    nullptr &&
×
709
                (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
×
710
                     nullptr ||
×
711
                 GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
×
712
                     nullptr))
713
            {
714
                fprintf(stderr, "  %s: %s\n", GDALGetDriverShortName(hDriver),
×
715
                        GDALGetDriverLongName(hDriver));
716
            }
717
        }
718
        printf("\n");
×
719
        return nullptr;
×
720
    }
721

722
    /* -------------------------------------------------------------------- */
723
    /*      Create target raster file.                                      */
724
    /* -------------------------------------------------------------------- */
725
    int nLayerCount = psOptions->aosLayers.size();
183✔
726
    if (nLayerCount == 0 && psOptions->osSQL.empty())
183✔
727
        nLayerCount = 1; /* due to above check */
119✔
728

729
    int nBands = nLayerCount;
183✔
730

731
    if (!psOptions->osSQL.empty())
183✔
732
        nBands++;
6✔
733

734
    int nXSize;
735
    int nYSize;
736
    if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0)
183✔
737
    {
738
        double dfXSize = (std::fabs(psOptions->dfXMax - psOptions->dfXMin) +
2✔
739
                          (psOptions->dfXRes / 2.0)) /
2✔
740
                         psOptions->dfXRes;
2✔
741
        double dfYSize = (std::fabs(psOptions->dfYMax - psOptions->dfYMin) +
2✔
742
                          (psOptions->dfYRes / 2.0)) /
2✔
743
                         psOptions->dfYRes;
2✔
744

745
        if (dfXSize >= 1 && dfXSize <= INT_MAX && dfYSize >= 1 &&
2✔
746
            dfYSize <= INT_MAX)
747
        {
748
            nXSize = static_cast<int>(dfXSize);
2✔
749
            nYSize = static_cast<int>(dfYSize);
2✔
750
        }
751
        else
752
        {
753
            CPLError(
×
754
                CE_Failure, CPLE_IllegalArg,
755
                "Invalid output size detected. Please check your -tr argument");
756

757
            if (pbUsageError)
×
758
                *pbUsageError = TRUE;
×
759
            return nullptr;
×
760
        }
2✔
761
    }
762
    else
763
    {
764
        // FIXME
765
        nXSize = psOptions->nXSize;
181✔
766
        if (nXSize == 0)
181✔
767
            nXSize = 256;
75✔
768
        nYSize = psOptions->nYSize;
181✔
769
        if (nYSize == 0)
181✔
770
            nYSize = 256;
75✔
771
    }
772

773
    std::unique_ptr<GDALDataset> poDstDS(GDALDataset::FromHandle(GDALCreate(
774
        hDriver, pszDest, nXSize, nYSize, nBands, psOptions->eOutputType,
183✔
775
        psOptions->aosCreateOptions.List())));
366✔
776
    if (!poDstDS)
183✔
777
    {
778
        return nullptr;
×
779
    }
780

781
    if (psOptions->bNoDataSet)
183✔
782
    {
783
        for (int i = 1; i <= nBands; i++)
258✔
784
        {
785
            poDstDS->GetRasterBand(i)->SetNoDataValue(psOptions->dfNoDataValue);
129✔
786
        }
787
    }
788

789
    double dfXMin = psOptions->dfXMin;
183✔
790
    double dfYMin = psOptions->dfYMin;
183✔
791
    double dfXMax = psOptions->dfXMax;
183✔
792
    double dfYMax = psOptions->dfYMax;
183✔
793
    bool bIsXExtentSet = psOptions->bIsXExtentSet;
183✔
794
    bool bIsYExtentSet = psOptions->bIsYExtentSet;
183✔
795
    CPLErr eErr = CE_None;
183✔
796

797
    /* -------------------------------------------------------------------- */
798
    /*      Process SQL request.                                            */
799
    /* -------------------------------------------------------------------- */
800

801
    if (!psOptions->osSQL.empty())
183✔
802
    {
803
        OGRLayer *poLayer =
804
            poSrcDS->ExecuteSQL(psOptions->osSQL.c_str(),
6✔
805
                                psOptions->poSpatialFilter.get(), nullptr);
6✔
806
        if (poLayer == nullptr)
6✔
807
        {
808
            return nullptr;
2✔
809
        }
810

811
        // Custom layer will be rasterized in the first band.
812
        eErr = ProcessLayer(
4✔
813
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
4✔
814
            nYSize, 1, bIsXExtentSet, bIsYExtentSet, dfXMin, dfXMax, dfYMin,
815
            dfYMax, psOptions->osBurnAttribute, psOptions->dfIncreaseBurnValue,
4✔
816
            psOptions->dfMultiplyBurnValue, psOptions->eOutputType,
4✔
817
            psOptions->eAlgorithm, psOptions->pOptions.get(), psOptions->bQuiet,
4✔
818
            psOptions->pfnProgress, psOptions->pProgressData);
4✔
819

820
        poSrcDS->ReleaseResultSet(poLayer);
4✔
821
    }
822

823
    /* -------------------------------------------------------------------- */
824
    /*      Process each layer.                                             */
825
    /* -------------------------------------------------------------------- */
826
    std::string osOutputSRS(psOptions->osOutputSRS);
362✔
827
    for (int i = 0; i < nLayerCount; i++)
355✔
828
    {
829
        auto poLayer = psOptions->aosLayers.empty()
177✔
830
                           ? poSrcDS->GetLayer(0)
177✔
831
                           : poSrcDS->GetLayerByName(psOptions->aosLayers[i]);
58✔
832
        if (!poLayer)
177✔
833
        {
834
            CPLError(CE_Failure, CPLE_AppDefined,
2✔
835
                     "Unable to find layer \"%s\".",
836
                     !psOptions->aosLayers.empty() && psOptions->aosLayers[i]
2✔
837
                         ? psOptions->aosLayers[i]
2✔
838
                         : "null");
839
            eErr = CE_Failure;
2✔
840
            break;
2✔
841
        }
842

843
        if (!psOptions->osWHERE.empty())
175✔
844
        {
845
            if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
2✔
846
                OGRERR_NONE)
847
            {
848
                eErr = CE_Failure;
1✔
849
                break;
1✔
850
            }
851
        }
852

853
        if (psOptions->poSpatialFilter)
174✔
854
            poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
4✔
855

856
        // Fetch the first meaningful SRS definition
857
        if (osOutputSRS.empty())
174✔
858
        {
859
            auto poSRS = poLayer->GetSpatialRef();
173✔
860
            if (poSRS)
173✔
861
                osOutputSRS = poSRS->exportToWkt();
91✔
862
        }
863

864
        eErr = ProcessLayer(
174✔
865
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
174✔
866
            nYSize, i + 1 + nBands - nLayerCount, bIsXExtentSet, bIsYExtentSet,
174✔
867
            dfXMin, dfXMax, dfYMin, dfYMax, psOptions->osBurnAttribute,
174✔
868
            psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
174✔
869
            psOptions->eOutputType, psOptions->eAlgorithm,
174✔
870
            psOptions->pOptions.get(), psOptions->bQuiet,
174✔
871
            psOptions->pfnProgress, psOptions->pProgressData);
174✔
872
        if (eErr != CE_None)
174✔
873
            break;
×
874
    }
875

876
    /* -------------------------------------------------------------------- */
877
    /*      Apply geotransformation matrix.                                 */
878
    /* -------------------------------------------------------------------- */
879
    poDstDS->SetGeoTransform(
362✔
880
        GDALGeoTransform(dfXMin, (dfXMax - dfXMin) / nXSize, 0.0, dfYMin, 0.0,
×
881
                         (dfYMax - dfYMin) / nYSize));
181✔
882

883
    /* -------------------------------------------------------------------- */
884
    /*      Apply SRS definition if set.                                    */
885
    /* -------------------------------------------------------------------- */
886
    if (!osOutputSRS.empty())
181✔
887
    {
888
        poDstDS->SetProjection(osOutputSRS.c_str());
92✔
889
    }
890

891
    /* -------------------------------------------------------------------- */
892
    /*      End                                                             */
893
    /* -------------------------------------------------------------------- */
894

895
    if (eErr != CE_None)
181✔
896
    {
897
        return nullptr;
3✔
898
    }
899

900
    return GDALDataset::ToHandle(poDstDS.release());
178✔
901
}
902

903
/************************************************************************/
904
/*                       GDALGridOptionsGetParser()                     */
905
/************************************************************************/
906

907
/*! @cond Doxygen_Suppress */
908

909
static std::unique_ptr<GDALArgumentParser>
910
GDALGridOptionsGetParser(GDALGridOptions *psOptions,
183✔
911
                         GDALGridOptionsForBinary *psOptionsForBinary,
912
                         int nCountClipSrc)
913
{
914
    auto argParser = std::make_unique<GDALArgumentParser>(
915
        "gdal_grid", /* bForBinary=*/psOptionsForBinary != nullptr);
183✔
916

917
    argParser->add_description(
183✔
918
        _("Creates a regular grid (raster) from the scattered data read from a "
919
          "vector datasource."));
183✔
920

921
    argParser->add_epilog(_(
183✔
922
        "Available algorithms and parameters with their defaults:\n"
923
        "    Inverse distance to a power (default)\n"
924
        "        "
925
        "invdist:power=2.0:smoothing=0.0:radius1=0.0:radius2=0.0:angle=0.0:max_"
926
        "points=0:min_points=0:nodata=0.0\n"
927
        "    Inverse distance to a power with nearest neighbor search\n"
928
        "        "
929
        "invdistnn:power=2.0:radius=1.0:max_points=12:min_points=0:nodata=0\n"
930
        "    Moving average\n"
931
        "        "
932
        "average:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
933
        "    Nearest neighbor\n"
934
        "        nearest:radius1=0.0:radius2=0.0:angle=0.0:nodata=0.0\n"
935
        "    Various data metrics\n"
936
        "        <metric "
937
        "name>:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
938
        "        possible metrics are:\n"
939
        "            minimum\n"
940
        "            maximum\n"
941
        "            range\n"
942
        "            count\n"
943
        "            average_distance\n"
944
        "            average_distance_pts\n"
945
        "    Linear\n"
946
        "        linear:radius=-1.0:nodata=0.0\n"
947
        "\n"
948
        "For more details, consult https://gdal.org/programs/gdal_grid.html"));
183✔
949

950
    argParser->add_quiet_argument(
951
        psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
183✔
952

953
    argParser->add_output_format_argument(psOptions->osFormat);
183✔
954

955
    argParser->add_output_type_argument(psOptions->eOutputType);
183✔
956

957
    argParser->add_argument("-txe")
183✔
958
        .metavar("<xmin> <xmax>")
366✔
959
        .nargs(2)
183✔
960
        .scan<'g', double>()
183✔
961
        .help(_("Set georeferenced X extents of output file to be created."));
183✔
962

963
    argParser->add_argument("-tye")
183✔
964
        .metavar("<ymin> <ymax>")
366✔
965
        .nargs(2)
183✔
966
        .scan<'g', double>()
183✔
967
        .help(_("Set georeferenced Y extents of output file to be created."));
183✔
968

969
    argParser->add_argument("-outsize")
183✔
970
        .metavar("<xsize> <ysize>")
366✔
971
        .nargs(2)
183✔
972
        .scan<'i', int>()
183✔
973
        .help(_("Set the size of the output file."));
183✔
974

975
    argParser->add_argument("-tr")
183✔
976
        .metavar("<xres> <yes>")
366✔
977
        .nargs(2)
183✔
978
        .scan<'g', double>()
183✔
979
        .help(_("Set target resolution."));
183✔
980

981
    argParser->add_creation_options_argument(psOptions->aosCreateOptions);
183✔
982

983
    argParser->add_argument("-zfield")
183✔
984
        .metavar("<field_name>")
366✔
985
        .store_into(psOptions->osBurnAttribute)
183✔
986
        .help(_("Field name from which to get Z values."));
183✔
987

988
    argParser->add_argument("-z_increase")
183✔
989
        .metavar("<increase_value>")
366✔
990
        .store_into(psOptions->dfIncreaseBurnValue)
183✔
991
        .help(_("Addition to the attribute field on the features to be used to "
992
                "get a Z value from."));
183✔
993

994
    argParser->add_argument("-z_multiply")
183✔
995
        .metavar("<multiply_value>")
366✔
996
        .store_into(psOptions->dfMultiplyBurnValue)
183✔
997
        .help(_("Multiplication ratio for the Z field.."));
183✔
998

999
    argParser->add_argument("-where")
183✔
1000
        .metavar("<expression>")
366✔
1001
        .store_into(psOptions->osWHERE)
183✔
1002
        .help(_("Query expression to be applied to select features to process "
1003
                "from the input layer(s)."));
183✔
1004

1005
    argParser->add_argument("-l")
183✔
1006
        .metavar("<layer_name>")
366✔
1007
        .append()
183✔
1008
        .action([psOptions](const std::string &s)
58✔
1009
                { psOptions->aosLayers.AddString(s.c_str()); })
241✔
1010
        .help(_("Layer(s) from the datasource that will be used for input "
1011
                "features."));
183✔
1012

1013
    argParser->add_argument("-sql")
183✔
1014
        .metavar("<select_statement>")
366✔
1015
        .store_into(psOptions->osSQL)
183✔
1016
        .help(_("SQL statement to be evaluated to produce a layer of features "
1017
                "to be processed."));
183✔
1018

1019
    argParser->add_argument("-spat")
183✔
1020
        .metavar("<xmin> <ymin> <xmax> <ymax>")
366✔
1021
        .nargs(4)
183✔
1022
        .scan<'g', double>()
183✔
1023
        .help(_("The area of interest. Only features within the rectangle will "
1024
                "be reported."));
183✔
1025

1026
    argParser->add_argument("-clipsrc")
183✔
1027
        .nargs(nCountClipSrc)
183✔
1028
        .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>|spat_extent")
366✔
1029
        .help(_("Clip geometries (in source SRS)."));
183✔
1030

1031
    argParser->add_argument("-clipsrcsql")
183✔
1032
        .metavar("<sql_statement>")
366✔
1033
        .store_into(psOptions->osClipSrcSQL)
183✔
1034
        .help(_("Select desired geometries from the source clip datasource "
1035
                "using an SQL query."));
183✔
1036

1037
    argParser->add_argument("-clipsrclayer")
183✔
1038
        .metavar("<layername>")
366✔
1039
        .store_into(psOptions->osClipSrcLayer)
183✔
1040
        .help(_("Select the named layer from the source clip datasource."));
183✔
1041

1042
    argParser->add_argument("-clipsrcwhere")
183✔
1043
        .metavar("<expression>")
366✔
1044
        .store_into(psOptions->osClipSrcWhere)
183✔
1045
        .help(_("Restrict desired geometries from the source clip layer based "
1046
                "on an attribute query."));
183✔
1047

1048
    argParser->add_argument("-a_srs")
183✔
1049
        .metavar("<srs_def>")
366✔
1050
        .action(
1051
            [psOptions](const std::string &osOutputSRSDef)
2✔
1052
            {
1053
                OGRSpatialReference oOutputSRS;
2✔
1054

1055
                if (oOutputSRS.SetFromUserInput(osOutputSRSDef.c_str()) !=
1✔
1056
                    OGRERR_NONE)
1057
                {
1058
                    throw std::invalid_argument(
1059
                        std::string("Failed to process SRS definition: ")
×
1060
                            .append(osOutputSRSDef));
×
1061
                }
1062

1063
                char *pszWKT = nullptr;
1✔
1064
                oOutputSRS.exportToWkt(&pszWKT);
1✔
1065
                if (pszWKT)
1✔
1066
                    psOptions->osOutputSRS = pszWKT;
1✔
1067
                CPLFree(pszWKT);
1✔
1068
            })
184✔
1069
        .help(_("Assign an output SRS, but without reprojecting."));
183✔
1070

1071
    argParser->add_argument("-a")
183✔
1072
        .metavar("<algorithm>[[:<parameter1>=<value1>]...]")
366✔
1073
        .action(
1074
            [psOptions](const std::string &s)
660✔
1075
            {
1076
                const char *pszAlgorithm = s.c_str();
177✔
1077
                void *pOptions = nullptr;
177✔
1078
                if (GDALGridParseAlgorithmAndOptions(pszAlgorithm,
177✔
1079
                                                     &psOptions->eAlgorithm,
1080
                                                     &pOptions) != CE_None)
177✔
1081
                {
1082
                    throw std::invalid_argument(
1083
                        "Failed to process algorithm name and parameters");
×
1084
                }
1085
                psOptions->pOptions.reset(pOptions);
177✔
1086

1087
                const CPLStringList aosParams(
1088
                    CSLTokenizeString2(pszAlgorithm, ":", FALSE));
354✔
1089
                const char *pszNoDataValue = aosParams.FetchNameValue("nodata");
177✔
1090
                if (pszNoDataValue != nullptr)
177✔
1091
                {
1092
                    psOptions->bNoDataSet = true;
129✔
1093
                    psOptions->dfNoDataValue = CPLAtofM(pszNoDataValue);
129✔
1094
                }
1095
            })
360✔
1096
        .help(_("Set the interpolation algorithm or data metric name and "
1097
                "(optionally) its parameters."));
183✔
1098

1099
    if (psOptionsForBinary)
183✔
1100
    {
1101
        argParser->add_open_options_argument(
1102
            &(psOptionsForBinary->aosOpenOptions));
54✔
1103
    }
1104

1105
    if (psOptionsForBinary)
183✔
1106
    {
1107
        argParser->add_argument("src_dataset_name")
54✔
1108
            .metavar("<src_dataset_name>")
108✔
1109
            .store_into(psOptionsForBinary->osSource)
54✔
1110
            .help(_("Input dataset."));
54✔
1111

1112
        argParser->add_argument("dst_dataset_name")
54✔
1113
            .metavar("<dst_dataset_name>")
108✔
1114
            .store_into(psOptionsForBinary->osDest)
54✔
1115
            .help(_("Output dataset."));
54✔
1116
    }
1117

1118
    return argParser;
183✔
1119
}
1120

1121
/*! @endcond */
1122

1123
/************************************************************************/
1124
/*                         GDALGridGetParserUsage()                     */
1125
/************************************************************************/
1126

1127
std::string GDALGridGetParserUsage()
×
1128
{
1129
    try
1130
    {
1131
        GDALGridOptions sOptions;
×
1132
        GDALGridOptionsForBinary sOptionsForBinary;
×
1133
        auto argParser =
1134
            GDALGridOptionsGetParser(&sOptions, &sOptionsForBinary, 1);
×
1135
        return argParser->usage();
×
1136
    }
1137
    catch (const std::exception &err)
×
1138
    {
1139
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
×
1140
                 err.what());
×
1141
        return std::string();
×
1142
    }
1143
}
1144

1145
/************************************************************************/
1146
/*                   CHECK_HAS_ENOUGH_ADDITIONAL_ARGS()                 */
1147
/************************************************************************/
1148

1149
#ifndef CheckHasEnoughAdditionalArgs_defined
1150
#define CheckHasEnoughAdditionalArgs_defined
1151

1152
static bool CheckHasEnoughAdditionalArgs(CSLConstList papszArgv, int i,
3✔
1153
                                         int nExtraArg, int nArgc)
1154
{
1155
    if (i + nExtraArg >= nArgc)
3✔
1156
    {
1157
        CPLError(CE_Failure, CPLE_IllegalArg,
×
1158
                 "%s option requires %d argument%s", papszArgv[i], nExtraArg,
×
1159
                 nExtraArg == 1 ? "" : "s");
1160
        return false;
×
1161
    }
1162
    return true;
3✔
1163
}
1164
#endif
1165

1166
#define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg)                            \
1167
    if (!CheckHasEnoughAdditionalArgs(papszArgv, i, nExtraArg, nArgc))         \
1168
    {                                                                          \
1169
        return nullptr;                                                        \
1170
    }
1171

1172
/************************************************************************/
1173
/*                             GDALGridOptionsNew()                     */
1174
/************************************************************************/
1175

1176
/**
1177
 * Allocates a GDALGridOptions struct.
1178
 *
1179
 * @param papszArgv NULL terminated list of options (potentially including
1180
 * filename and open options too), or NULL. The accepted options are the ones of
1181
 * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
1182
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1183
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1184
 *                           GDALGridOptionsForBinaryNew() prior to this
1185
 * function. Will be filled with potentially present filename, open options,...
1186
 * @return pointer to the allocated GDALGridOptions struct. Must be freed with
1187
 * GDALGridOptionsFree().
1188
 *
1189
 * @since GDAL 2.1
1190
 */
1191

1192
GDALGridOptions *
1193
GDALGridOptionsNew(char **papszArgv,
183✔
1194
                   GDALGridOptionsForBinary *psOptionsForBinary)
1195
{
1196
    auto psOptions = std::make_unique<GDALGridOptions>();
366✔
1197

1198
    /* -------------------------------------------------------------------- */
1199
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
1200
    /*      support.                                                        */
1201
    /* -------------------------------------------------------------------- */
1202

1203
    CPLStringList aosArgv;
366✔
1204
    const int nArgc = CSLCount(papszArgv);
183✔
1205
    int nCountClipSrc = 0;
183✔
1206
    for (int i = 0;
2,491✔
1207
         i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
2,491✔
1208
    {
1209
        if (EQUAL(papszArgv[i], "-clipsrc"))
2,308✔
1210
        {
1211
            if (nCountClipSrc)
3✔
1212
            {
1213
                CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
×
1214
                         papszArgv[i]);
×
1215
                return nullptr;
×
1216
            }
1217
            // argparse doesn't handle well variable number of values
1218
            // just before the positional arguments, so we have to detect
1219
            // it manually and set the correct number.
1220
            nCountClipSrc = 1;
3✔
1221
            CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
3✔
1222
            if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
5✔
1223
                i + 4 < nArgc)
2✔
1224
            {
1225
                nCountClipSrc = 4;
2✔
1226
            }
1227

1228
            for (int j = 0; j < 1 + nCountClipSrc; ++j)
15✔
1229
            {
1230
                aosArgv.AddString(papszArgv[i]);
12✔
1231
                ++i;
12✔
1232
            }
1233
            --i;
3✔
1234
        }
1235

1236
        else
1237
        {
1238
            aosArgv.AddString(papszArgv[i]);
2,305✔
1239
        }
1240
    }
1241

1242
    try
1243
    {
1244
        auto argParser = GDALGridOptionsGetParser(
1245
            psOptions.get(), psOptionsForBinary, nCountClipSrc);
366✔
1246

1247
        argParser->parse_args_without_binary_name(aosArgv.List());
183✔
1248

1249
        if (auto oTXE = argParser->present<std::vector<double>>("-txe"))
295✔
1250
        {
1251
            psOptions->dfXMin = (*oTXE)[0];
112✔
1252
            psOptions->dfXMax = (*oTXE)[1];
112✔
1253
            psOptions->bIsXExtentSet = true;
112✔
1254
        }
1255

1256
        if (auto oTYE = argParser->present<std::vector<double>>("-tye"))
295✔
1257
        {
1258
            psOptions->dfYMin = (*oTYE)[0];
112✔
1259
            psOptions->dfYMax = (*oTYE)[1];
112✔
1260
            psOptions->bIsYExtentSet = true;
112✔
1261
        }
1262

1263
        if (auto oOutsize = argParser->present<std::vector<int>>("-outsize"))
289✔
1264
        {
1265
            psOptions->nXSize = (*oOutsize)[0];
106✔
1266
            psOptions->nYSize = (*oOutsize)[1];
106✔
1267
        }
1268

1269
        if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
183✔
1270
        {
1271
            psOptions->dfXRes = (*adfTargetRes)[0];
2✔
1272
            psOptions->dfYRes = (*adfTargetRes)[1];
2✔
1273
            if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
2✔
1274
            {
1275
                CPLError(CE_Failure, CPLE_IllegalArg,
×
1276
                         "Wrong value for -tr parameters.");
1277
                return nullptr;
×
1278
            }
1279
        }
1280

1281
        if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
184✔
1282
        {
1283
            OGRLinearRing oRing;
2✔
1284
            const double dfMinX = (*oSpat)[0];
1✔
1285
            const double dfMinY = (*oSpat)[1];
1✔
1286
            const double dfMaxX = (*oSpat)[2];
1✔
1287
            const double dfMaxY = (*oSpat)[3];
1✔
1288

1289
            oRing.addPoint(dfMinX, dfMinY);
1✔
1290
            oRing.addPoint(dfMinX, dfMaxY);
1✔
1291
            oRing.addPoint(dfMaxX, dfMaxY);
1✔
1292
            oRing.addPoint(dfMaxX, dfMinY);
1✔
1293
            oRing.addPoint(dfMinX, dfMinY);
1✔
1294

1295
            auto poPolygon = std::make_unique<OGRPolygon>();
2✔
1296
            poPolygon->addRing(&oRing);
1✔
1297
            psOptions->poSpatialFilter = std::move(poPolygon);
1✔
1298
        }
1299

1300
        if (auto oClipSrc =
183✔
1301
                argParser->present<std::vector<std::string>>("-clipsrc"))
183✔
1302
        {
1303
            const std::string &osVal = (*oClipSrc)[0];
3✔
1304

1305
            psOptions->poClipSrc.reset();
3✔
1306
            psOptions->osClipSrcDS.clear();
3✔
1307

1308
            VSIStatBufL sStat;
1309
            psOptions->bClipSrc = true;
3✔
1310
            if (oClipSrc->size() == 4)
3✔
1311
            {
1312
                const double dfMinX = CPLAtofM((*oClipSrc)[0].c_str());
2✔
1313
                const double dfMinY = CPLAtofM((*oClipSrc)[1].c_str());
2✔
1314
                const double dfMaxX = CPLAtofM((*oClipSrc)[2].c_str());
2✔
1315
                const double dfMaxY = CPLAtofM((*oClipSrc)[3].c_str());
2✔
1316

1317
                OGRLinearRing oRing;
4✔
1318

1319
                oRing.addPoint(dfMinX, dfMinY);
2✔
1320
                oRing.addPoint(dfMinX, dfMaxY);
2✔
1321
                oRing.addPoint(dfMaxX, dfMaxY);
2✔
1322
                oRing.addPoint(dfMaxX, dfMinY);
2✔
1323
                oRing.addPoint(dfMinX, dfMinY);
2✔
1324

1325
                auto poPoly = std::make_unique<OGRPolygon>();
4✔
1326
                poPoly->addRing(&oRing);
2✔
1327
                psOptions->poClipSrc = std::move(poPoly);
2✔
1328
            }
1329
            else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
1✔
1330
                      STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
1✔
1331
                     VSIStatL(osVal.c_str(), &sStat) != 0)
×
1332
            {
1333
                psOptions->poClipSrc =
×
1334
                    OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr)
×
1335
                        .first;
×
1336
                if (psOptions->poClipSrc == nullptr)
×
1337
                {
1338
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
1339
                             "Invalid geometry. Must be a valid POLYGON or "
1340
                             "MULTIPOLYGON WKT");
1341
                    return nullptr;
×
1342
                }
1343
            }
1344
            else if (EQUAL(osVal.c_str(), "spat_extent"))
1✔
1345
            {
1346
                // Nothing to do
1347
            }
1348
            else
1349
            {
1350
                psOptions->osClipSrcDS = osVal;
1✔
1351
            }
1352
        }
1353

1354
        if (psOptions->bClipSrc && !psOptions->osClipSrcDS.empty())
183✔
1355
        {
1356
            psOptions->poClipSrc = LoadGeometry(
2✔
1357
                psOptions->osClipSrcDS, psOptions->osClipSrcSQL,
1✔
1358
                psOptions->osClipSrcLayer, psOptions->osClipSrcWhere);
2✔
1359
            if (!psOptions->poClipSrc)
1✔
1360
            {
1361
                CPLError(CE_Failure, CPLE_AppDefined,
×
1362
                         "Cannot load source clip geometry.");
1363
                return nullptr;
×
1364
            }
1365
        }
1366
        else if (psOptions->bClipSrc && !psOptions->poClipSrc &&
182✔
1367
                 !psOptions->poSpatialFilter)
×
1368
        {
1369
            CPLError(CE_Failure, CPLE_AppDefined,
×
1370
                     "-clipsrc must be used with -spat option or \n"
1371
                     "a bounding box, WKT string or datasource must be "
1372
                     "specified.");
1373
            return nullptr;
×
1374
        }
1375

1376
        if (psOptions->poSpatialFilter)
183✔
1377
        {
1378
            if (psOptions->poClipSrc)
1✔
1379
            {
1380
                auto poTemp = std::unique_ptr<OGRGeometry>(
1381
                    psOptions->poSpatialFilter->Intersection(
×
1382
                        psOptions->poClipSrc.get()));
×
1383
                if (poTemp)
×
1384
                {
1385
                    psOptions->poSpatialFilter = std::move(poTemp);
×
1386
                }
1387

1388
                psOptions->poClipSrc.reset();
×
1389
            }
1390
        }
1391
        else
1392
        {
1393
            if (psOptions->poClipSrc)
182✔
1394
            {
1395
                psOptions->poSpatialFilter = std::move(psOptions->poClipSrc);
3✔
1396
            }
1397
        }
1398

1399
        if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0 &&
185✔
1400
            !(psOptions->bIsXExtentSet && psOptions->bIsYExtentSet))
2✔
1401
        {
1402
            CPLError(CE_Failure, CPLE_IllegalArg,
×
1403
                     "-txe ad -tye arguments must be provided when "
1404
                     "resolution is provided.");
1405
            return nullptr;
×
1406
        }
1407

1408
        return psOptions.release();
183✔
1409
    }
1410
    catch (const std::exception &err)
×
1411
    {
1412
        CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
×
1413
        return nullptr;
×
1414
    }
1415
}
1416

1417
/************************************************************************/
1418
/*                          GDALGridOptionsFree()                       */
1419
/************************************************************************/
1420

1421
/**
1422
 * Frees the GDALGridOptions struct.
1423
 *
1424
 * @param psOptions the options struct for GDALGrid().
1425
 *
1426
 * @since GDAL 2.1
1427
 */
1428

1429
void GDALGridOptionsFree(GDALGridOptions *psOptions)
183✔
1430
{
1431
    delete psOptions;
183✔
1432
}
183✔
1433

1434
/************************************************************************/
1435
/*                     GDALGridOptionsSetProgress()                     */
1436
/************************************************************************/
1437

1438
/**
1439
 * Set a progress function.
1440
 *
1441
 * @param psOptions the options struct for GDALGrid().
1442
 * @param pfnProgress the progress callback.
1443
 * @param pProgressData the user data for the progress callback.
1444
 *
1445
 * @since GDAL 2.1
1446
 */
1447

1448
void GDALGridOptionsSetProgress(GDALGridOptions *psOptions,
131✔
1449
                                GDALProgressFunc pfnProgress,
1450
                                void *pProgressData)
1451
{
1452
    psOptions->pfnProgress = pfnProgress;
131✔
1453
    psOptions->pProgressData = pProgressData;
131✔
1454
    if (pfnProgress == GDALTermProgress)
131✔
1455
        psOptions->bQuiet = false;
54✔
1456
}
131✔
1457

1458
#undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc