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

OSGeo / gdal / 13872211292

15 Mar 2025 11:00AM UTC coverage: 70.445% (+0.009%) from 70.436%
13872211292

Pull #11951

github

web-flow
Merge 643845942 into bb4e0ed67
Pull Request #11951: Doc: Build docs using CMake

553795 of 786140 relevant lines covered (70.44%)

221892.63 hits per line

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

75.35
/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()
106✔
97
    {
106✔
98
        void *l_pOptions = nullptr;
106✔
99
        GDALGridParseAlgorithmAndOptions(szAlgNameInvDist, &eAlgorithm,
106✔
100
                                         &l_pOptions);
101
        pOptions.reset(l_pOptions);
106✔
102
    }
106✔
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
64,928✔
278
    {
279
        if (poClipSrc && !p->Within(poClipSrc))
64,928✔
280
            return;
20✔
281

282
        if (iBurnField < 0 && std::isnan(p->getZ()))
64,908✔
283
            return;
1✔
284

285
        adfX.push_back(p->getX());
64,907✔
286
        adfY.push_back(p->getY());
64,907✔
287
        if (iBurnField < 0)
64,907✔
288
            adfZ.push_back((p->getZ() + dfIncreaseBurnValue) *
129,806✔
289
                           dfMultiplyBurnValue);
64,903✔
290
        else
291
            adfZ.push_back((dfBurnValue + dfIncreaseBurnValue) *
4✔
292
                           dfMultiplyBurnValue);
4✔
293
    }
294
};
295

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

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

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

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

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

342
    for (auto &&poFeat : poSrcLayer)
64,882✔
343
    {
344
        const OGRGeometry *poGeom = poFeat->GetGeometryRef();
64,779✔
345
        if (poGeom)
64,779✔
346
        {
347
            if (iBurnField >= 0)
64,779✔
348
            {
349
                if (!poFeat->IsFieldSetAndNotNull(iBurnField))
5✔
350
                {
351
                    continue;
1✔
352
                }
353
                oVisitor.dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
4✔
354
            }
355

356
            poGeom->accept(&oVisitor);
64,778✔
357
        }
358
    }
359

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

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

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

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

393
    // Produce north-up images
394
    if (dfYMin < dfYMax)
103✔
395
        std::swap(dfYMin, dfYMax);
51✔
396

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

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

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

417
    GDALRasterBand *poBand = poDstDS->GetRasterBand(nBand);
103✔
418

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

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

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

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

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

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

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

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

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

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

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

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

519
    return eErr;
103✔
520
}
521

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

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

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

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

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

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

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

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

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

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

598
    return poGeom;
1✔
599
}
600

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

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

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

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

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

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

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

657
    GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
106✔
658

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

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

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

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

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

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

728
    int nBands = nLayerCount;
106✔
729

730
    if (!psOptions->osSQL.empty())
106✔
731
        nBands++;
4✔
732

733
    int nXSize;
734
    int nYSize;
735
    if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0)
106✔
736
    {
737
        if ((psOptions->dfXMax == psOptions->dfXMin) ||
1✔
738
            (psOptions->dfYMax == psOptions->dfYMin))
1✔
739
        {
740
            CPLError(CE_Failure, CPLE_IllegalArg,
×
741
                     "Invalid txe or tye parameters detected. Please check "
742
                     "your -txe or -tye argument.");
743

744
            if (pbUsageError)
×
745
                *pbUsageError = TRUE;
×
746
            return nullptr;
×
747
        }
748

749
        double dfXSize = (std::fabs(psOptions->dfXMax - psOptions->dfXMin) +
1✔
750
                          (psOptions->dfXRes / 2.0)) /
1✔
751
                         psOptions->dfXRes;
1✔
752
        double dfYSize = (std::fabs(psOptions->dfYMax - psOptions->dfYMin) +
1✔
753
                          (psOptions->dfYRes / 2.0)) /
1✔
754
                         psOptions->dfYRes;
1✔
755

756
        if (dfXSize >= 1 && dfXSize <= INT_MAX && dfYSize >= 1 &&
1✔
757
            dfYSize <= INT_MAX)
758
        {
759
            nXSize = static_cast<int>(dfXSize);
1✔
760
            nYSize = static_cast<int>(dfYSize);
1✔
761
        }
762
        else
763
        {
764
            CPLError(
×
765
                CE_Failure, CPLE_IllegalArg,
766
                "Invalid output size detected. Please check your -tr argument");
767

768
            if (pbUsageError)
×
769
                *pbUsageError = TRUE;
×
770
            return nullptr;
×
771
        }
1✔
772
    }
773
    else
774
    {
775
        // FIXME
776
        nXSize = psOptions->nXSize;
105✔
777
        if (nXSize == 0)
105✔
778
            nXSize = 256;
×
779
        nYSize = psOptions->nYSize;
105✔
780
        if (nYSize == 0)
105✔
781
            nYSize = 256;
×
782
    }
783

784
    std::unique_ptr<GDALDataset> poDstDS(GDALDataset::FromHandle(GDALCreate(
785
        hDriver, pszDest, nXSize, nYSize, nBands, psOptions->eOutputType,
106✔
786
        psOptions->aosCreateOptions.List())));
212✔
787
    if (!poDstDS)
106✔
788
    {
789
        return nullptr;
×
790
    }
791

792
    if (psOptions->bNoDataSet)
106✔
793
    {
794
        for (int i = 1; i <= nBands; i++)
104✔
795
        {
796
            poDstDS->GetRasterBand(i)->SetNoDataValue(psOptions->dfNoDataValue);
52✔
797
        }
798
    }
799

800
    double dfXMin = psOptions->dfXMin;
106✔
801
    double dfYMin = psOptions->dfYMin;
106✔
802
    double dfXMax = psOptions->dfXMax;
106✔
803
    double dfYMax = psOptions->dfYMax;
106✔
804
    bool bIsXExtentSet = psOptions->bIsXExtentSet;
106✔
805
    bool bIsYExtentSet = psOptions->bIsYExtentSet;
106✔
806
    CPLErr eErr = CE_None;
106✔
807

808
    /* -------------------------------------------------------------------- */
809
    /*      Process SQL request.                                            */
810
    /* -------------------------------------------------------------------- */
811

812
    if (!psOptions->osSQL.empty())
106✔
813
    {
814
        OGRLayer *poLayer =
815
            poSrcDS->ExecuteSQL(psOptions->osSQL.c_str(),
4✔
816
                                psOptions->poSpatialFilter.get(), nullptr);
4✔
817
        if (poLayer == nullptr)
4✔
818
        {
819
            return nullptr;
1✔
820
        }
821

822
        // Custom layer will be rasterized in the first band.
823
        eErr = ProcessLayer(
3✔
824
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
3✔
825
            nYSize, 1, bIsXExtentSet, bIsYExtentSet, dfXMin, dfXMax, dfYMin,
826
            dfYMax, psOptions->osBurnAttribute, psOptions->dfIncreaseBurnValue,
3✔
827
            psOptions->dfMultiplyBurnValue, psOptions->eOutputType,
3✔
828
            psOptions->eAlgorithm, psOptions->pOptions.get(), psOptions->bQuiet,
3✔
829
            psOptions->pfnProgress, psOptions->pProgressData);
3✔
830

831
        poSrcDS->ReleaseResultSet(poLayer);
3✔
832
    }
833

834
    /* -------------------------------------------------------------------- */
835
    /*      Process each layer.                                             */
836
    /* -------------------------------------------------------------------- */
837
    std::string osOutputSRS(psOptions->osOutputSRS);
210✔
838
    for (int i = 0; i < nLayerCount; i++)
205✔
839
    {
840
        auto poLayer = psOptions->aosLayers.empty()
102✔
841
                           ? poSrcDS->GetLayer(0)
102✔
842
                           : poSrcDS->GetLayerByName(psOptions->aosLayers[i]);
55✔
843
        if (!poLayer)
102✔
844
        {
845
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
846
                     "Unable to find layer \"%s\".",
847
                     !psOptions->aosLayers.empty() && psOptions->aosLayers[i]
1✔
848
                         ? psOptions->aosLayers[i]
1✔
849
                         : "null");
850
            eErr = CE_Failure;
1✔
851
            break;
1✔
852
        }
853

854
        if (!psOptions->osWHERE.empty())
101✔
855
        {
856
            if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
2✔
857
                OGRERR_NONE)
858
            {
859
                eErr = CE_Failure;
1✔
860
                break;
1✔
861
            }
862
        }
863

864
        if (psOptions->poSpatialFilter)
100✔
865
            poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
2✔
866

867
        // Fetch the first meaningful SRS definition
868
        if (osOutputSRS.empty())
100✔
869
        {
870
            auto poSRS = poLayer->GetSpatialRef();
100✔
871
            if (poSRS)
100✔
872
                osOutputSRS = poSRS->exportToWkt();
91✔
873
        }
874

875
        eErr = ProcessLayer(
100✔
876
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
100✔
877
            nYSize, i + 1 + nBands - nLayerCount, bIsXExtentSet, bIsYExtentSet,
100✔
878
            dfXMin, dfXMax, dfYMin, dfYMax, psOptions->osBurnAttribute,
100✔
879
            psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
100✔
880
            psOptions->eOutputType, psOptions->eAlgorithm,
100✔
881
            psOptions->pOptions.get(), psOptions->bQuiet,
100✔
882
            psOptions->pfnProgress, psOptions->pProgressData);
100✔
883
        if (eErr != CE_None)
100✔
884
            break;
×
885
    }
886

887
    /* -------------------------------------------------------------------- */
888
    /*      Apply geotransformation matrix.                                 */
889
    /* -------------------------------------------------------------------- */
890
    double adfGeoTransform[6] = {dfXMin, (dfXMax - dfXMin) / nXSize,
105✔
891
                                 0.0,    dfYMin,
892
                                 0.0,    (dfYMax - dfYMin) / nYSize};
105✔
893
    poDstDS->SetGeoTransform(adfGeoTransform);
105✔
894

895
    /* -------------------------------------------------------------------- */
896
    /*      Apply SRS definition if set.                                    */
897
    /* -------------------------------------------------------------------- */
898
    if (!osOutputSRS.empty())
105✔
899
    {
900
        poDstDS->SetProjection(osOutputSRS.c_str());
91✔
901
    }
902

903
    /* -------------------------------------------------------------------- */
904
    /*      End                                                             */
905
    /* -------------------------------------------------------------------- */
906

907
    if (eErr != CE_None)
105✔
908
    {
909
        return nullptr;
2✔
910
    }
911

912
    return GDALDataset::ToHandle(poDstDS.release());
103✔
913
}
914

915
/************************************************************************/
916
/*                       GDALGridOptionsGetParser()                     */
917
/************************************************************************/
918

919
/*! @cond Doxygen_Suppress */
920

921
static std::unique_ptr<GDALArgumentParser>
922
GDALGridOptionsGetParser(GDALGridOptions *psOptions,
106✔
923
                         GDALGridOptionsForBinary *psOptionsForBinary,
924
                         int nCountClipSrc)
925
{
926
    auto argParser = std::make_unique<GDALArgumentParser>(
927
        "gdal_grid", /* bForBinary=*/psOptionsForBinary != nullptr);
106✔
928

929
    argParser->add_description(
106✔
930
        _("Creates a regular grid (raster) from the scattered data read from a "
931
          "vector datasource."));
106✔
932

933
    argParser->add_epilog(_(
106✔
934
        "Available algorithms and parameters with their defaults:\n"
935
        "    Inverse distance to a power (default)\n"
936
        "        "
937
        "invdist:power=2.0:smoothing=0.0:radius1=0.0:radius2=0.0:angle=0.0:max_"
938
        "points=0:min_points=0:nodata=0.0\n"
939
        "    Inverse distance to a power with nearest neighbor search\n"
940
        "        "
941
        "invdistnn:power=2.0:radius=1.0:max_points=12:min_points=0:nodata=0\n"
942
        "    Moving average\n"
943
        "        "
944
        "average:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
945
        "    Nearest neighbor\n"
946
        "        nearest:radius1=0.0:radius2=0.0:angle=0.0:nodata=0.0\n"
947
        "    Various data metrics\n"
948
        "        <metric "
949
        "name>:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
950
        "        possible metrics are:\n"
951
        "            minimum\n"
952
        "            maximum\n"
953
        "            range\n"
954
        "            count\n"
955
        "            average_distance\n"
956
        "            average_distance_pts\n"
957
        "    Linear\n"
958
        "        linear:radius=-1.0:nodata=0.0\n"
959
        "\n"
960
        "For more details, consult https://gdal.org/programs/gdal_grid.html"));
106✔
961

962
    argParser->add_quiet_argument(
963
        psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
106✔
964

965
    argParser->add_output_format_argument(psOptions->osFormat);
106✔
966

967
    argParser->add_output_type_argument(psOptions->eOutputType);
106✔
968

969
    argParser->add_argument("-txe")
106✔
970
        .metavar("<xmin> <xmax>")
212✔
971
        .nargs(2)
106✔
972
        .scan<'g', double>()
106✔
973
        .help(_("Set georeferenced X extents of output file to be created."));
106✔
974

975
    argParser->add_argument("-tye")
106✔
976
        .metavar("<ymin> <ymax>")
212✔
977
        .nargs(2)
106✔
978
        .scan<'g', double>()
106✔
979
        .help(_("Set georeferenced Y extents of output file to be created."));
106✔
980

981
    argParser->add_argument("-outsize")
106✔
982
        .metavar("<xsize> <ysize>")
212✔
983
        .nargs(2)
106✔
984
        .scan<'i', int>()
106✔
985
        .help(_("Set the size of the output file."));
106✔
986

987
    argParser->add_argument("-tr")
106✔
988
        .metavar("<xres> <yes>")
212✔
989
        .nargs(2)
106✔
990
        .scan<'g', double>()
106✔
991
        .help(_("Set target resolution."));
106✔
992

993
    argParser->add_creation_options_argument(psOptions->aosCreateOptions);
106✔
994

995
    argParser->add_argument("-zfield")
106✔
996
        .metavar("<field_name>")
212✔
997
        .store_into(psOptions->osBurnAttribute)
106✔
998
        .help(_("Field name from which to get Z values."));
106✔
999

1000
    argParser->add_argument("-z_increase")
106✔
1001
        .metavar("<increase_value>")
212✔
1002
        .store_into(psOptions->dfIncreaseBurnValue)
106✔
1003
        .help(_("Addition to the attribute field on the features to be used to "
1004
                "get a Z value from."));
106✔
1005

1006
    argParser->add_argument("-z_multiply")
106✔
1007
        .metavar("<multiply_value>")
212✔
1008
        .store_into(psOptions->dfMultiplyBurnValue)
106✔
1009
        .help(_("Multiplication ratio for the Z field.."));
106✔
1010

1011
    argParser->add_argument("-where")
106✔
1012
        .metavar("<expression>")
212✔
1013
        .store_into(psOptions->osWHERE)
106✔
1014
        .help(_("Query expression to be applied to select features to process "
1015
                "from the input layer(s)."));
106✔
1016

1017
    argParser->add_argument("-l")
106✔
1018
        .metavar("<layer_name>")
212✔
1019
        .append()
106✔
1020
        .action([psOptions](const std::string &s)
55✔
1021
                { psOptions->aosLayers.AddString(s.c_str()); })
161✔
1022
        .help(_("Layer(s) from the datasource that will be used for input "
1023
                "features."));
106✔
1024

1025
    argParser->add_argument("-sql")
106✔
1026
        .metavar("<select_statement>")
212✔
1027
        .store_into(psOptions->osSQL)
106✔
1028
        .help(_("SQL statement to be evaluated to produce a layer of features "
1029
                "to be processed."));
106✔
1030

1031
    argParser->add_argument("-spat")
106✔
1032
        .metavar("<xmin> <ymin> <xmax> <ymax>")
212✔
1033
        .nargs(4)
106✔
1034
        .scan<'g', double>()
106✔
1035
        .help(_("The area of interest. Only features within the rectangle will "
1036
                "be reported."));
106✔
1037

1038
    argParser->add_argument("-clipsrc")
106✔
1039
        .nargs(nCountClipSrc)
106✔
1040
        .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>|spat_extent")
212✔
1041
        .help(_("Clip geometries (in source SRS)."));
106✔
1042

1043
    argParser->add_argument("-clipsrcsql")
106✔
1044
        .metavar("<sql_statement>")
212✔
1045
        .store_into(psOptions->osClipSrcSQL)
106✔
1046
        .help(_("Select desired geometries from the source clip datasource "
1047
                "using an SQL query."));
106✔
1048

1049
    argParser->add_argument("-clipsrclayer")
106✔
1050
        .metavar("<layername>")
212✔
1051
        .store_into(psOptions->osClipSrcLayer)
106✔
1052
        .help(_("Select the named layer from the source clip datasource."));
106✔
1053

1054
    argParser->add_argument("-clipsrcwhere")
106✔
1055
        .metavar("<expression>")
212✔
1056
        .store_into(psOptions->osClipSrcWhere)
106✔
1057
        .help(_("Restrict desired geometries from the source clip layer based "
1058
                "on an attribute query."));
106✔
1059

1060
    argParser->add_argument("-a_srs")
106✔
1061
        .metavar("<srs_def>")
212✔
1062
        .action(
1063
            [psOptions](const std::string &osOutputSRSDef)
×
1064
            {
1065
                OGRSpatialReference oOutputSRS;
×
1066

1067
                if (oOutputSRS.SetFromUserInput(osOutputSRSDef.c_str()) !=
×
1068
                    OGRERR_NONE)
1069
                {
1070
                    throw std::invalid_argument(
1071
                        std::string("Failed to process SRS definition: ")
×
1072
                            .append(osOutputSRSDef));
×
1073
                }
1074

1075
                char *pszWKT = nullptr;
×
1076
                oOutputSRS.exportToWkt(&pszWKT);
×
1077
                if (pszWKT)
×
1078
                    psOptions->osOutputSRS = pszWKT;
×
1079
                CPLFree(pszWKT);
×
1080
            })
106✔
1081
        .help(_("Assign an output SRS, but without reprojecting."));
106✔
1082

1083
    argParser->add_argument("-a")
106✔
1084
        .metavar("<algorithm>[[:<parameter1>=<value1>]...]")
212✔
1085
        .action(
1086
            [psOptions](const std::string &s)
352✔
1087
            {
1088
                const char *pszAlgorithm = s.c_str();
100✔
1089
                void *pOptions = nullptr;
100✔
1090
                if (GDALGridParseAlgorithmAndOptions(pszAlgorithm,
100✔
1091
                                                     &psOptions->eAlgorithm,
1092
                                                     &pOptions) != CE_None)
100✔
1093
                {
1094
                    throw std::invalid_argument(
1095
                        "Failed to process algorithm name and parameters");
×
1096
                }
1097
                psOptions->pOptions.reset(pOptions);
100✔
1098

1099
                const CPLStringList aosParams(
1100
                    CSLTokenizeString2(pszAlgorithm, ":", FALSE));
200✔
1101
                const char *pszNoDataValue = aosParams.FetchNameValue("nodata");
100✔
1102
                if (pszNoDataValue != nullptr)
100✔
1103
                {
1104
                    psOptions->bNoDataSet = true;
52✔
1105
                    psOptions->dfNoDataValue = CPLAtofM(pszNoDataValue);
52✔
1106
                }
1107
            })
206✔
1108
        .help(_("Set the interpolation algorithm or data metric name and "
1109
                "(optionally) its parameters."));
106✔
1110

1111
    if (psOptionsForBinary)
106✔
1112
    {
1113
        argParser->add_open_options_argument(
1114
            &(psOptionsForBinary->aosOpenOptions));
54✔
1115
    }
1116

1117
    if (psOptionsForBinary)
106✔
1118
    {
1119
        argParser->add_argument("src_dataset_name")
54✔
1120
            .metavar("<src_dataset_name>")
108✔
1121
            .store_into(psOptionsForBinary->osSource)
54✔
1122
            .help(_("Input dataset."));
54✔
1123

1124
        argParser->add_argument("dst_dataset_name")
54✔
1125
            .metavar("<dst_dataset_name>")
108✔
1126
            .store_into(psOptionsForBinary->osDest)
54✔
1127
            .help(_("Output dataset."));
54✔
1128
    }
1129

1130
    return argParser;
106✔
1131
}
1132

1133
/*! @endcond */
1134

1135
/************************************************************************/
1136
/*                         GDALGridGetParserUsage()                     */
1137
/************************************************************************/
1138

1139
std::string GDALGridGetParserUsage()
×
1140
{
1141
    try
1142
    {
1143
        GDALGridOptions sOptions;
×
1144
        GDALGridOptionsForBinary sOptionsForBinary;
×
1145
        auto argParser =
1146
            GDALGridOptionsGetParser(&sOptions, &sOptionsForBinary, 1);
×
1147
        return argParser->usage();
×
1148
    }
1149
    catch (const std::exception &err)
×
1150
    {
1151
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
×
1152
                 err.what());
×
1153
        return std::string();
×
1154
    }
1155
}
1156

1157
/************************************************************************/
1158
/*                   CHECK_HAS_ENOUGH_ADDITIONAL_ARGS()                 */
1159
/************************************************************************/
1160

1161
#ifndef CheckHasEnoughAdditionalArgs_defined
1162
#define CheckHasEnoughAdditionalArgs_defined
1163

1164
static bool CheckHasEnoughAdditionalArgs(CSLConstList papszArgv, int i,
1✔
1165
                                         int nExtraArg, int nArgc)
1166
{
1167
    if (i + nExtraArg >= nArgc)
1✔
1168
    {
1169
        CPLError(CE_Failure, CPLE_IllegalArg,
×
1170
                 "%s option requires %d argument%s", papszArgv[i], nExtraArg,
×
1171
                 nExtraArg == 1 ? "" : "s");
1172
        return false;
×
1173
    }
1174
    return true;
1✔
1175
}
1176
#endif
1177

1178
#define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg)                            \
1179
    if (!CheckHasEnoughAdditionalArgs(papszArgv, i, nExtraArg, nArgc))         \
1180
    {                                                                          \
1181
        return nullptr;                                                        \
1182
    }
1183

1184
/************************************************************************/
1185
/*                             GDALGridOptionsNew()                     */
1186
/************************************************************************/
1187

1188
/**
1189
 * Allocates a GDALGridOptions struct.
1190
 *
1191
 * @param papszArgv NULL terminated list of options (potentially including
1192
 * filename and open options too), or NULL. The accepted options are the ones of
1193
 * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
1194
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1195
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1196
 *                           GDALGridOptionsForBinaryNew() prior to this
1197
 * function. Will be filled with potentially present filename, open options,...
1198
 * @return pointer to the allocated GDALGridOptions struct. Must be freed with
1199
 * GDALGridOptionsFree().
1200
 *
1201
 * @since GDAL 2.1
1202
 */
1203

1204
GDALGridOptions *
1205
GDALGridOptionsNew(char **papszArgv,
106✔
1206
                   GDALGridOptionsForBinary *psOptionsForBinary)
1207
{
1208
    auto psOptions = std::make_unique<GDALGridOptions>();
212✔
1209

1210
    /* -------------------------------------------------------------------- */
1211
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
1212
    /*      support.                                                        */
1213
    /* -------------------------------------------------------------------- */
1214

1215
    CPLStringList aosArgv;
212✔
1216
    const int nArgc = CSLCount(papszArgv);
106✔
1217
    int nCountClipSrc = 0;
106✔
1218
    for (int i = 0;
1,742✔
1219
         i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
1,742✔
1220
    {
1221
        if (EQUAL(papszArgv[i], "-clipsrc"))
1,636✔
1222
        {
1223
            if (nCountClipSrc)
1✔
1224
            {
1225
                CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
×
1226
                         papszArgv[i]);
×
1227
                return nullptr;
×
1228
            }
1229
            // argparse doesn't handle well variable number of values
1230
            // just before the positional arguments, so we have to detect
1231
            // it manually and set the correct number.
1232
            nCountClipSrc = 1;
1✔
1233
            CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
1✔
1234
            if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
1✔
1235
                i + 4 < nArgc)
×
1236
            {
1237
                nCountClipSrc = 4;
×
1238
            }
1239

1240
            for (int j = 0; j < 1 + nCountClipSrc; ++j)
3✔
1241
            {
1242
                aosArgv.AddString(papszArgv[i]);
2✔
1243
                ++i;
2✔
1244
            }
1245
            --i;
1✔
1246
        }
1247

1248
        else
1249
        {
1250
            aosArgv.AddString(papszArgv[i]);
1,635✔
1251
        }
1252
    }
1253

1254
    try
1255
    {
1256
        auto argParser = GDALGridOptionsGetParser(
1257
            psOptions.get(), psOptionsForBinary, nCountClipSrc);
212✔
1258

1259
        argParser->parse_args_without_binary_name(aosArgv.List());
106✔
1260

1261
        if (auto oTXE = argParser->present<std::vector<double>>("-txe"))
212✔
1262
        {
1263
            psOptions->dfXMin = (*oTXE)[0];
106✔
1264
            psOptions->dfXMax = (*oTXE)[1];
106✔
1265
            psOptions->bIsXExtentSet = true;
106✔
1266
        }
1267

1268
        if (auto oTYE = argParser->present<std::vector<double>>("-tye"))
212✔
1269
        {
1270
            psOptions->dfYMin = (*oTYE)[0];
106✔
1271
            psOptions->dfYMax = (*oTYE)[1];
106✔
1272
            psOptions->bIsYExtentSet = true;
106✔
1273
        }
1274

1275
        if (auto oOutsize = argParser->present<std::vector<int>>("-outsize"))
211✔
1276
        {
1277
            psOptions->nXSize = (*oOutsize)[0];
105✔
1278
            psOptions->nYSize = (*oOutsize)[1];
105✔
1279
        }
1280

1281
        if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
106✔
1282
        {
1283
            psOptions->dfXRes = (*adfTargetRes)[0];
1✔
1284
            psOptions->dfYRes = (*adfTargetRes)[1];
1✔
1285
            if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
1✔
1286
            {
1287
                CPLError(CE_Failure, CPLE_IllegalArg,
×
1288
                         "Wrong value for -tr parameters.");
1289
                return nullptr;
×
1290
            }
1291
        }
1292

1293
        if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
107✔
1294
        {
1295
            OGRLinearRing oRing;
2✔
1296
            const double dfMinX = (*oSpat)[0];
1✔
1297
            const double dfMinY = (*oSpat)[1];
1✔
1298
            const double dfMaxX = (*oSpat)[2];
1✔
1299
            const double dfMaxY = (*oSpat)[3];
1✔
1300

1301
            oRing.addPoint(dfMinX, dfMinY);
1✔
1302
            oRing.addPoint(dfMinX, dfMaxY);
1✔
1303
            oRing.addPoint(dfMaxX, dfMaxY);
1✔
1304
            oRing.addPoint(dfMaxX, dfMinY);
1✔
1305
            oRing.addPoint(dfMinX, dfMinY);
1✔
1306

1307
            auto poPolygon = std::make_unique<OGRPolygon>();
2✔
1308
            poPolygon->addRing(&oRing);
1✔
1309
            psOptions->poSpatialFilter = std::move(poPolygon);
1✔
1310
        }
1311

1312
        if (auto oClipSrc =
106✔
1313
                argParser->present<std::vector<std::string>>("-clipsrc"))
106✔
1314
        {
1315
            const std::string &osVal = (*oClipSrc)[0];
1✔
1316

1317
            psOptions->poClipSrc.reset();
1✔
1318
            psOptions->osClipSrcDS.clear();
1✔
1319

1320
            VSIStatBufL sStat;
1321
            psOptions->bClipSrc = true;
1✔
1322
            if (oClipSrc->size() == 4)
1✔
1323
            {
1324
                const double dfMinX = CPLAtofM((*oClipSrc)[0].c_str());
×
1325
                const double dfMinY = CPLAtofM((*oClipSrc)[1].c_str());
×
1326
                const double dfMaxX = CPLAtofM((*oClipSrc)[2].c_str());
×
1327
                const double dfMaxY = CPLAtofM((*oClipSrc)[3].c_str());
×
1328

1329
                OGRLinearRing oRing;
×
1330

1331
                oRing.addPoint(dfMinX, dfMinY);
×
1332
                oRing.addPoint(dfMinX, dfMaxY);
×
1333
                oRing.addPoint(dfMaxX, dfMaxY);
×
1334
                oRing.addPoint(dfMaxX, dfMinY);
×
1335
                oRing.addPoint(dfMinX, dfMinY);
×
1336

1337
                auto poPoly = std::make_unique<OGRPolygon>();
×
1338
                poPoly->addRing(&oRing);
×
1339
                psOptions->poClipSrc = std::move(poPoly);
×
1340
            }
1341
            else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
1✔
1342
                      STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
1✔
1343
                     VSIStatL(osVal.c_str(), &sStat) != 0)
×
1344
            {
1345
                psOptions->poClipSrc =
×
1346
                    OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr)
×
1347
                        .first;
×
1348
                if (psOptions->poClipSrc == nullptr)
×
1349
                {
1350
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
1351
                             "Invalid geometry. Must be a valid POLYGON or "
1352
                             "MULTIPOLYGON WKT");
1353
                    return nullptr;
×
1354
                }
1355
            }
1356
            else if (EQUAL(osVal.c_str(), "spat_extent"))
1✔
1357
            {
1358
                // Nothing to do
1359
            }
1360
            else
1361
            {
1362
                psOptions->osClipSrcDS = osVal;
1✔
1363
            }
1364
        }
1365

1366
        if (psOptions->bClipSrc && !psOptions->osClipSrcDS.empty())
106✔
1367
        {
1368
            psOptions->poClipSrc = LoadGeometry(
2✔
1369
                psOptions->osClipSrcDS, psOptions->osClipSrcSQL,
1✔
1370
                psOptions->osClipSrcLayer, psOptions->osClipSrcWhere);
2✔
1371
            if (!psOptions->poClipSrc)
1✔
1372
            {
1373
                CPLError(CE_Failure, CPLE_AppDefined,
×
1374
                         "Cannot load source clip geometry.");
1375
                return nullptr;
×
1376
            }
1377
        }
1378
        else if (psOptions->bClipSrc && !psOptions->poClipSrc &&
105✔
1379
                 !psOptions->poSpatialFilter)
×
1380
        {
1381
            CPLError(CE_Failure, CPLE_AppDefined,
×
1382
                     "-clipsrc must be used with -spat option or \n"
1383
                     "a bounding box, WKT string or datasource must be "
1384
                     "specified.");
1385
            return nullptr;
×
1386
        }
1387

1388
        if (psOptions->poSpatialFilter)
106✔
1389
        {
1390
            if (psOptions->poClipSrc)
1✔
1391
            {
1392
                auto poTemp = std::unique_ptr<OGRGeometry>(
1393
                    psOptions->poSpatialFilter->Intersection(
×
1394
                        psOptions->poClipSrc.get()));
×
1395
                if (poTemp)
×
1396
                {
1397
                    psOptions->poSpatialFilter = std::move(poTemp);
×
1398
                }
1399

1400
                psOptions->poClipSrc.reset();
×
1401
            }
1402
        }
1403
        else
1404
        {
1405
            if (psOptions->poClipSrc)
105✔
1406
            {
1407
                psOptions->poSpatialFilter = std::move(psOptions->poClipSrc);
1✔
1408
            }
1409
        }
1410

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

1420
/************************************************************************/
1421
/*                          GDALGridOptionsFree()                       */
1422
/************************************************************************/
1423

1424
/**
1425
 * Frees the GDALGridOptions struct.
1426
 *
1427
 * @param psOptions the options struct for GDALGrid().
1428
 *
1429
 * @since GDAL 2.1
1430
 */
1431

1432
void GDALGridOptionsFree(GDALGridOptions *psOptions)
106✔
1433
{
1434
    delete psOptions;
106✔
1435
}
106✔
1436

1437
/************************************************************************/
1438
/*                     GDALGridOptionsSetProgress()                     */
1439
/************************************************************************/
1440

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

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

1461
#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