Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdal_grid_lib.cpp
Line
Count
Source
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()
97
0
    {
98
0
        void *l_pOptions = nullptr;
99
0
        GDALGridParseAlgorithmAndOptions(szAlgNameInvDist, &eAlgorithm,
100
0
                                         &l_pOptions);
101
0
        pOptions.reset(l_pOptions);
102
0
    }
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,
114
                                     void *pOptions)
115
0
{
116
0
    switch (eAlgorithm)
117
0
    {
118
0
        case GGA_InverseDistanceToAPower:
119
0
        {
120
0
            printf("Algorithm name: \"%s\".\n", szAlgNameInvDist);
121
0
            GDALGridInverseDistanceToAPowerOptions *pOptions2 =
122
0
                static_cast<GDALGridInverseDistanceToAPowerOptions *>(pOptions);
123
0
            CPLprintf("Options are "
124
0
                      "\"power=%f:smoothing=%f:radius1=%f:radius2=%f:angle=%f"
125
0
                      ":max_points=%u:min_points=%u:nodata=%f\"\n",
126
0
                      pOptions2->dfPower, pOptions2->dfSmoothing,
127
0
                      pOptions2->dfRadius1, pOptions2->dfRadius2,
128
0
                      pOptions2->dfAngle, pOptions2->nMaxPoints,
129
0
                      pOptions2->nMinPoints, pOptions2->dfNoDataValue);
130
0
            break;
131
0
        }
132
0
        case GGA_InverseDistanceToAPowerNearestNeighbor:
133
0
        {
134
0
            printf("Algorithm name: \"%s\".\n",
135
0
                   szAlgNameInvDistNearestNeighbor);
136
0
            GDALGridInverseDistanceToAPowerNearestNeighborOptions *pOptions2 =
137
0
                static_cast<
138
0
                    GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
139
0
                    pOptions);
140
0
            CPLString osStr;
141
0
            osStr.Printf("power=%f:smoothing=%f:radius=%f"
142
0
                         ":max_points=%u:min_points=%u:nodata=%f",
143
0
                         pOptions2->dfPower, pOptions2->dfSmoothing,
144
0
                         pOptions2->dfRadius, pOptions2->nMaxPoints,
145
0
                         pOptions2->nMinPoints, pOptions2->dfNoDataValue);
146
0
            if (pOptions2->nMinPointsPerQuadrant > 0)
147
0
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
148
0
                                    pOptions2->nMinPointsPerQuadrant);
149
0
            if (pOptions2->nMaxPointsPerQuadrant > 0)
150
0
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
151
0
                                    pOptions2->nMaxPointsPerQuadrant);
152
0
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
153
0
            break;
154
0
        }
155
0
        case GGA_MovingAverage:
156
0
        {
157
0
            printf("Algorithm name: \"%s\".\n", szAlgNameAverage);
158
0
            GDALGridMovingAverageOptions *pOptions2 =
159
0
                static_cast<GDALGridMovingAverageOptions *>(pOptions);
160
0
            CPLString osStr;
161
0
            osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
162
0
                         ":nodata=%f",
163
0
                         pOptions2->dfRadius1, pOptions2->dfRadius2,
164
0
                         pOptions2->dfAngle, pOptions2->nMinPoints,
165
0
                         pOptions2->dfNoDataValue);
166
0
            if (pOptions2->nMinPointsPerQuadrant > 0)
167
0
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
168
0
                                    pOptions2->nMinPointsPerQuadrant);
169
0
            if (pOptions2->nMaxPointsPerQuadrant > 0)
170
0
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
171
0
                                    pOptions2->nMaxPointsPerQuadrant);
172
0
            if (pOptions2->nMaxPoints > 0)
173
0
                osStr += CPLSPrintf(":max_points=%u", pOptions2->nMaxPoints);
174
0
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
175
0
            break;
176
0
        }
177
0
        case GGA_NearestNeighbor:
178
0
        {
179
0
            printf("Algorithm name: \"%s\".\n", szAlgNameNearest);
180
0
            GDALGridNearestNeighborOptions *pOptions2 =
181
0
                static_cast<GDALGridNearestNeighborOptions *>(pOptions);
182
0
            CPLprintf("Options are "
183
0
                      "\"radius1=%f:radius2=%f:angle=%f:nodata=%f\"\n",
184
0
                      pOptions2->dfRadius1, pOptions2->dfRadius2,
185
0
                      pOptions2->dfAngle, pOptions2->dfNoDataValue);
186
0
            break;
187
0
        }
188
0
        case GGA_MetricMinimum:
189
0
        case GGA_MetricMaximum:
190
0
        case GGA_MetricRange:
191
0
        case GGA_MetricCount:
192
0
        case GGA_MetricAverageDistance:
193
0
        case GGA_MetricAverageDistancePts:
194
0
        {
195
0
            const char *pszAlgName = "";
196
0
            CPL_IGNORE_RET_VAL(pszAlgName);  // Make CSA happy
197
0
            switch (eAlgorithm)
198
0
            {
199
0
                case GGA_MetricMinimum:
200
0
                    pszAlgName = szAlgNameMinimum;
201
0
                    break;
202
0
                case GGA_MetricMaximum:
203
0
                    pszAlgName = szAlgNameMaximum;
204
0
                    break;
205
0
                case GGA_MetricRange:
206
0
                    pszAlgName = szAlgNameRange;
207
0
                    break;
208
0
                case GGA_MetricCount:
209
0
                    pszAlgName = szAlgNameCount;
210
0
                    break;
211
0
                case GGA_MetricAverageDistance:
212
0
                    pszAlgName = szAlgNameAverageDistance;
213
0
                    break;
214
0
                case GGA_MetricAverageDistancePts:
215
0
                    pszAlgName = szAlgNameAverageDistancePts;
216
0
                    break;
217
0
                default:
218
0
                    CPLAssert(false);
219
0
                    break;
220
0
            }
221
0
            printf("Algorithm name: \"%s\".\n", pszAlgName);
222
0
            GDALGridDataMetricsOptions *pOptions2 =
223
0
                static_cast<GDALGridDataMetricsOptions *>(pOptions);
224
0
            CPLString osStr;
225
0
            osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
226
0
                         ":nodata=%f",
227
0
                         pOptions2->dfRadius1, pOptions2->dfRadius2,
228
0
                         pOptions2->dfAngle, pOptions2->nMinPoints,
229
0
                         pOptions2->dfNoDataValue);
230
0
            if (pOptions2->nMinPointsPerQuadrant > 0)
231
0
                osStr += CPLSPrintf(":min_points_per_quadrant=%u",
232
0
                                    pOptions2->nMinPointsPerQuadrant);
233
0
            if (pOptions2->nMaxPointsPerQuadrant > 0)
234
0
                osStr += CPLSPrintf(":max_points_per_quadrant=%u",
235
0
                                    pOptions2->nMaxPointsPerQuadrant);
236
0
            printf("Options are: \"%s\n", osStr.c_str()); /* ok */
237
0
            break;
238
0
        }
239
0
        case GGA_Linear:
240
0
        {
241
0
            printf("Algorithm name: \"%s\".\n", szAlgNameLinear);
242
0
            GDALGridLinearOptions *pOptions2 =
243
0
                static_cast<GDALGridLinearOptions *>(pOptions);
244
0
            CPLprintf("Options are "
245
0
                      "\"radius=%f:nodata=%f\"\n",
246
0
                      pOptions2->dfRadius, pOptions2->dfNoDataValue);
247
0
            break;
248
0
        }
249
0
        default:
250
0
        {
251
0
            printf("Algorithm is unknown.\n");
252
0
            break;
253
0
        }
254
0
    }
255
0
}
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)
281
0
{
282
0
    if (poClipSrc && !p->Within(poClipSrc))
283
0
        return;
284
285
0
    if (iBurnField < 0 && std::isnan(p->getZ()))
286
0
        return;
287
288
0
    adfX.push_back(p->getX());
289
0
    adfY.push_back(p->getY());
290
0
    if (iBurnField < 0)
291
0
        adfZ.push_back((p->getZ() + dfIncreaseBurnValue) * dfMultiplyBurnValue);
292
0
    else
293
0
        adfZ.push_back((dfBurnValue + dfIncreaseBurnValue) *
294
0
                       dfMultiplyBurnValue);
295
0
}
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,
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
0
{
316
    /* -------------------------------------------------------------------- */
317
    /*      Get field index, and check.                                     */
318
    /* -------------------------------------------------------------------- */
319
0
    int iBurnField = -1;
320
321
0
    if (!osBurnAttribute.empty())
322
0
    {
323
0
        iBurnField =
324
0
            poSrcLayer->GetLayerDefn()->GetFieldIndex(osBurnAttribute.c_str());
325
0
        if (iBurnField == -1)
326
0
        {
327
0
            printf("Failed to find field %s on layer %s, skipping.\n",
328
0
                   osBurnAttribute.c_str(), poSrcLayer->GetName());
329
0
            return CE_Failure;
330
0
        }
331
0
    }
332
333
    /* -------------------------------------------------------------------- */
334
    /*      Collect the geometries from this layer, and build list of       */
335
    /*      values to be interpolated.                                      */
336
    /* -------------------------------------------------------------------- */
337
0
    GDALGridGeometryVisitor oVisitor;
338
0
    oVisitor.poClipSrc = poClipSrc;
339
0
    oVisitor.iBurnField = iBurnField;
340
0
    oVisitor.dfIncreaseBurnValue = dfIncreaseBurnValue;
341
0
    oVisitor.dfMultiplyBurnValue = dfMultiplyBurnValue;
342
343
0
    for (auto &&poFeat : poSrcLayer)
344
0
    {
345
0
        const OGRGeometry *poGeom = poFeat->GetGeometryRef();
346
0
        if (poGeom)
347
0
        {
348
0
            if (iBurnField >= 0)
349
0
            {
350
0
                if (!poFeat->IsFieldSetAndNotNull(iBurnField))
351
0
                {
352
0
                    continue;
353
0
                }
354
0
                oVisitor.dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
355
0
            }
356
357
0
            poGeom->accept(&oVisitor);
358
0
        }
359
0
    }
360
361
0
    if (oVisitor.adfX.empty())
362
0
    {
363
0
        printf("No point geometry found on layer %s, skipping.\n",
364
0
               poSrcLayer->GetName());
365
0
        return CE_None;
366
0
    }
367
368
    /* -------------------------------------------------------------------- */
369
    /*      Compute grid geometry.                                          */
370
    /* -------------------------------------------------------------------- */
371
0
    if (!bIsXExtentSet || !bIsYExtentSet)
372
0
    {
373
0
        OGREnvelope sEnvelope;
374
0
        if (poSrcLayer->GetExtent(&sEnvelope, TRUE) == OGRERR_FAILURE)
375
0
        {
376
0
            return CE_Failure;
377
0
        }
378
379
0
        if (!bIsXExtentSet)
380
0
        {
381
0
            dfXMin = sEnvelope.MinX;
382
0
            dfXMax = sEnvelope.MaxX;
383
0
            bIsXExtentSet = true;
384
0
        }
385
386
0
        if (!bIsYExtentSet)
387
0
        {
388
0
            dfYMin = sEnvelope.MinY;
389
0
            dfYMax = sEnvelope.MaxY;
390
0
            bIsYExtentSet = true;
391
0
        }
392
0
    }
393
394
    // Produce north-up images
395
0
    if (dfYMin < dfYMax)
396
0
        std::swap(dfYMin, dfYMax);
397
398
    /* -------------------------------------------------------------------- */
399
    /*      Perform gridding.                                               */
400
    /* -------------------------------------------------------------------- */
401
402
0
    const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
403
0
    const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
404
405
0
    if (!bQuiet)
406
0
    {
407
0
        printf("Grid data type is \"%s\"\n", GDALGetDataTypeName(eType));
408
0
        printf("Grid size = (%d %d).\n", nXSize, nYSize);
409
0
        CPLprintf("Corner coordinates = (%f %f)-(%f %f).\n", dfXMin, dfYMin,
410
0
                  dfXMax, dfYMax);
411
0
        CPLprintf("Grid cell size = (%f %f).\n", dfDeltaX, dfDeltaY);
412
0
        printf("Source point count = %lu.\n",
413
0
               static_cast<unsigned long>(oVisitor.adfX.size()));
414
0
        PrintAlgorithmAndOptions(eAlgorithm, pOptions);
415
0
        printf("\n");
416
0
    }
417
418
0
    GDALRasterBand *poBand = poDstDS->GetRasterBand(nBand);
419
420
0
    int nBlockXSize = 0;
421
0
    int nBlockYSize = 0;
422
0
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
423
424
    // Try to grow the work buffer up to 16 MB if it is smaller
425
0
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
426
0
    if (nXSize == 0 || nYSize == 0 || nBlockXSize == 0 || nBlockYSize == 0)
427
0
        return CE_Failure;
428
429
0
    const int nDesiredBufferSize = 16 * 1024 * 1024;
430
0
    if (nBlockXSize < nXSize && nBlockYSize < nYSize &&
431
0
        nBlockXSize < nDesiredBufferSize / (nBlockYSize * nDataTypeSize))
432
0
    {
433
0
        const int nNewBlockXSize =
434
0
            nDesiredBufferSize / (nBlockYSize * nDataTypeSize);
435
0
        nBlockXSize = (nNewBlockXSize / nBlockXSize) * nBlockXSize;
436
0
        if (nBlockXSize > nXSize)
437
0
            nBlockXSize = nXSize;
438
0
    }
439
0
    else if (nBlockXSize == nXSize && nBlockYSize < nYSize &&
440
0
             nBlockYSize < nDesiredBufferSize / (nXSize * nDataTypeSize))
441
0
    {
442
0
        const int nNewBlockYSize =
443
0
            nDesiredBufferSize / (nXSize * nDataTypeSize);
444
0
        nBlockYSize = (nNewBlockYSize / nBlockYSize) * nBlockYSize;
445
0
        if (nBlockYSize > nYSize)
446
0
            nBlockYSize = nYSize;
447
0
    }
448
0
    CPLDebug("GDAL_GRID", "Work buffer: %d * %d", nBlockXSize, nBlockYSize);
449
450
0
    std::unique_ptr<void, VSIFreeReleaser> pData(
451
0
        VSIMalloc3(nBlockXSize, nBlockYSize, nDataTypeSize));
452
0
    if (!pData)
453
0
    {
454
0
        CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate work buffer");
455
0
        return CE_Failure;
456
0
    }
457
458
0
    GIntBig nBlock = 0;
459
0
    const double dfBlockCount =
460
0
        static_cast<double>(DIV_ROUND_UP(nXSize, nBlockXSize)) *
461
0
        DIV_ROUND_UP(nYSize, nBlockYSize);
462
463
0
    struct GDALGridContextReleaser
464
0
    {
465
0
        void operator()(GDALGridContext *psContext)
466
0
        {
467
0
            GDALGridContextFree(psContext);
468
0
        }
469
0
    };
470
471
0
    std::unique_ptr<GDALGridContext, GDALGridContextReleaser> psContext(
472
0
        GDALGridContextCreate(eAlgorithm, pOptions,
473
0
                              static_cast<int>(oVisitor.adfX.size()),
474
0
                              &(oVisitor.adfX[0]), &(oVisitor.adfY[0]),
475
0
                              &(oVisitor.adfZ[0]), TRUE));
476
0
    if (!psContext)
477
0
    {
478
0
        return CE_Failure;
479
0
    }
480
481
0
    CPLErr eErr = CE_None;
482
0
    for (int nYOffset = 0; nYOffset < nYSize && eErr == CE_None;
483
0
         nYOffset += nBlockYSize)
484
0
    {
485
0
        for (int nXOffset = 0; nXOffset < nXSize && eErr == CE_None;
486
0
             nXOffset += nBlockXSize)
487
0
        {
488
0
            std::unique_ptr<void, GDALScaledProgressReleaser> pScaledProgress(
489
0
                GDALCreateScaledProgress(
490
0
                    static_cast<double>(nBlock) / dfBlockCount,
491
0
                    static_cast<double>(nBlock + 1) / dfBlockCount, pfnProgress,
492
0
                    pProgressData));
493
0
            nBlock++;
494
495
0
            int nXRequest = nBlockXSize;
496
0
            if (nXOffset > nXSize - nXRequest)
497
0
                nXRequest = nXSize - nXOffset;
498
499
0
            int nYRequest = nBlockYSize;
500
0
            if (nYOffset > nYSize - nYRequest)
501
0
                nYRequest = nYSize - nYOffset;
502
503
0
            eErr = GDALGridContextProcess(
504
0
                psContext.get(), dfXMin + dfDeltaX * nXOffset,
505
0
                dfXMin + dfDeltaX * (nXOffset + nXRequest),
506
0
                dfYMin + dfDeltaY * nYOffset,
507
0
                dfYMin + dfDeltaY * (nYOffset + nYRequest), nXRequest,
508
0
                nYRequest, eType, pData.get(), GDALScaledProgress,
509
0
                pScaledProgress.get());
510
511
0
            if (eErr == CE_None)
512
0
                eErr = poBand->RasterIO(GF_Write, nXOffset, nYOffset, nXRequest,
513
0
                                        nYRequest, pData.get(), nXRequest,
514
0
                                        nYRequest, eType, 0, 0, nullptr);
515
0
        }
516
0
    }
517
0
    if (eErr == CE_None && pfnProgress)
518
0
        pfnProgress(1.0, "", pProgressData);
519
520
0
    return eErr;
521
0
}
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,
531
                                                 const std::string &osSQL,
532
                                                 const std::string &osLyr,
533
                                                 const std::string &osWhere)
534
0
{
535
0
    auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
536
0
        osDS.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
537
0
    if (!poDS)
538
0
        return nullptr;
539
540
0
    OGRLayer *poLyr = nullptr;
541
0
    if (!osSQL.empty())
542
0
        poLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
543
0
    else if (!osLyr.empty())
544
0
        poLyr = poDS->GetLayerByName(osLyr.c_str());
545
0
    else
546
0
        poLyr = poDS->GetLayer(0);
547
548
0
    if (poLyr == nullptr)
549
0
    {
550
0
        CPLError(CE_Failure, CPLE_AppDefined,
551
0
                 "Failed to identify source layer from datasource.");
552
0
        return nullptr;
553
0
    }
554
555
0
    if (!osWhere.empty())
556
0
        poLyr->SetAttributeFilter(osWhere.c_str());
557
558
0
    std::unique_ptr<OGRGeometryCollection> poGeom;
559
0
    for (auto &poFeat : poLyr)
560
0
    {
561
0
        const OGRGeometry *poSrcGeom = poFeat->GetGeometryRef();
562
0
        if (poSrcGeom)
563
0
        {
564
0
            const OGRwkbGeometryType eType =
565
0
                wkbFlatten(poSrcGeom->getGeometryType());
566
567
0
            if (!poGeom)
568
0
                poGeom = std::make_unique<OGRMultiPolygon>();
569
570
0
            if (eType == wkbPolygon)
571
0
            {
572
0
                poGeom->addGeometry(poSrcGeom);
573
0
            }
574
0
            else if (eType == wkbMultiPolygon)
575
0
            {
576
0
                const int nGeomCount =
577
0
                    poSrcGeom->toMultiPolygon()->getNumGeometries();
578
579
0
                for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
580
0
                {
581
0
                    poGeom->addGeometry(
582
0
                        poSrcGeom->toMultiPolygon()->getGeometryRef(iGeom));
583
0
                }
584
0
            }
585
0
            else
586
0
            {
587
0
                CPLError(CE_Failure, CPLE_AppDefined,
588
0
                         "Geometry not of polygon type.");
589
0
                if (!osSQL.empty())
590
0
                    poDS->ReleaseResultSet(poLyr);
591
0
                return nullptr;
592
0
            }
593
0
        }
594
0
    }
595
596
0
    if (!osSQL.empty())
597
0
        poDS->ReleaseResultSet(poLyr);
598
599
0
    return poGeom;
600
0
}
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,
630
                      const GDALGridOptions *psOptionsIn, int *pbUsageError)
631
632
0
{
633
0
    if (hSrcDataset == nullptr)
634
0
    {
635
0
        CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
636
637
0
        if (pbUsageError)
638
0
            *pbUsageError = TRUE;
639
0
        return nullptr;
640
0
    }
641
0
    if (pszDest == nullptr)
642
0
    {
643
0
        CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
644
645
0
        if (pbUsageError)
646
0
            *pbUsageError = TRUE;
647
0
        return nullptr;
648
0
    }
649
650
0
    std::unique_ptr<GDALGridOptions> psOptionsToFree;
651
0
    const GDALGridOptions *psOptions = psOptionsIn;
652
0
    if (psOptions == nullptr)
653
0
    {
654
0
        psOptionsToFree = std::make_unique<GDALGridOptions>();
655
0
        psOptions = psOptionsToFree.get();
656
0
    }
657
658
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
659
660
0
    if (psOptions->osSQL.empty() && psOptions->aosLayers.empty() &&
661
0
        poSrcDS->GetLayerCount() != 1)
662
0
    {
663
0
        CPLError(CE_Failure, CPLE_NotSupported,
664
0
                 "Neither -sql nor -l are specified, but the source dataset "
665
0
                 "has not one single layer.");
666
0
        if (pbUsageError)
667
0
            *pbUsageError = TRUE;
668
0
        return nullptr;
669
0
    }
670
671
0
    if ((psOptions->nXSize != 0 || psOptions->nYSize != 0) &&
672
0
        (psOptions->dfXRes != 0 || psOptions->dfYRes != 0))
673
0
    {
674
0
        CPLError(CE_Failure, CPLE_IllegalArg,
675
0
                 "-outsize and -tr options cannot be used at the same time.");
676
0
        return nullptr;
677
0
    }
678
679
    /* -------------------------------------------------------------------- */
680
    /*      Find the output driver.                                         */
681
    /* -------------------------------------------------------------------- */
682
0
    std::string osFormat;
683
0
    if (psOptions->osFormat.empty())
684
0
    {
685
0
        osFormat = GetOutputDriverForRaster(pszDest);
686
0
        if (osFormat.empty())
687
0
        {
688
0
            return nullptr;
689
0
        }
690
0
    }
691
0
    else
692
0
    {
693
0
        osFormat = psOptions->osFormat;
694
0
    }
695
696
0
    GDALDriverH hDriver = GDALGetDriverByName(osFormat.c_str());
697
0
    if (hDriver == nullptr)
698
0
    {
699
0
        CPLError(CE_Failure, CPLE_AppDefined,
700
0
                 "Output driver `%s' not recognised.", osFormat.c_str());
701
0
        fprintf(stderr, "The following format drivers are enabled and "
702
0
                        "support writing:\n");
703
0
        for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
704
0
        {
705
0
            hDriver = GDALGetDriver(iDr);
706
707
0
            if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
708
0
                    nullptr &&
709
0
                (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
710
0
                     nullptr ||
711
0
                 GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
712
0
                     nullptr))
713
0
            {
714
0
                fprintf(stderr, "  %s: %s\n", GDALGetDriverShortName(hDriver),
715
0
                        GDALGetDriverLongName(hDriver));
716
0
            }
717
0
        }
718
0
        printf("\n");
719
0
        return nullptr;
720
0
    }
721
722
    /* -------------------------------------------------------------------- */
723
    /*      Create target raster file.                                      */
724
    /* -------------------------------------------------------------------- */
725
0
    int nLayerCount = psOptions->aosLayers.size();
726
0
    if (nLayerCount == 0 && psOptions->osSQL.empty())
727
0
        nLayerCount = 1; /* due to above check */
728
729
0
    int nBands = nLayerCount;
730
731
0
    if (!psOptions->osSQL.empty())
732
0
        nBands++;
733
734
0
    int nXSize;
735
0
    int nYSize;
736
0
    if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0)
737
0
    {
738
0
        double dfXSize = (std::fabs(psOptions->dfXMax - psOptions->dfXMin) +
739
0
                          (psOptions->dfXRes / 2.0)) /
740
0
                         psOptions->dfXRes;
741
0
        double dfYSize = (std::fabs(psOptions->dfYMax - psOptions->dfYMin) +
742
0
                          (psOptions->dfYRes / 2.0)) /
743
0
                         psOptions->dfYRes;
744
745
0
        if (dfXSize >= 1 && dfXSize <= INT_MAX && dfYSize >= 1 &&
746
0
            dfYSize <= INT_MAX)
747
0
        {
748
0
            nXSize = static_cast<int>(dfXSize);
749
0
            nYSize = static_cast<int>(dfYSize);
750
0
        }
751
0
        else
752
0
        {
753
0
            CPLError(
754
0
                CE_Failure, CPLE_IllegalArg,
755
0
                "Invalid output size detected. Please check your -tr argument");
756
757
0
            if (pbUsageError)
758
0
                *pbUsageError = TRUE;
759
0
            return nullptr;
760
0
        }
761
0
    }
762
0
    else
763
0
    {
764
        // FIXME
765
0
        nXSize = psOptions->nXSize;
766
0
        if (nXSize == 0)
767
0
            nXSize = 256;
768
0
        nYSize = psOptions->nYSize;
769
0
        if (nYSize == 0)
770
0
            nYSize = 256;
771
0
    }
772
773
0
    std::unique_ptr<GDALDataset> poDstDS(GDALDataset::FromHandle(GDALCreate(
774
0
        hDriver, pszDest, nXSize, nYSize, nBands, psOptions->eOutputType,
775
0
        psOptions->aosCreateOptions.List())));
776
0
    if (!poDstDS)
777
0
    {
778
0
        return nullptr;
779
0
    }
780
781
0
    if (psOptions->bNoDataSet)
782
0
    {
783
0
        for (int i = 1; i <= nBands; i++)
784
0
        {
785
0
            poDstDS->GetRasterBand(i)->SetNoDataValue(psOptions->dfNoDataValue);
786
0
        }
787
0
    }
788
789
0
    double dfXMin = psOptions->dfXMin;
790
0
    double dfYMin = psOptions->dfYMin;
791
0
    double dfXMax = psOptions->dfXMax;
792
0
    double dfYMax = psOptions->dfYMax;
793
0
    bool bIsXExtentSet = psOptions->bIsXExtentSet;
794
0
    bool bIsYExtentSet = psOptions->bIsYExtentSet;
795
0
    CPLErr eErr = CE_None;
796
797
0
    const bool bCloseReportsProgress = poDstDS->GetCloseReportsProgress();
798
799
    /* -------------------------------------------------------------------- */
800
    /*      Process SQL request.                                            */
801
    /* -------------------------------------------------------------------- */
802
803
0
    if (!psOptions->osSQL.empty())
804
0
    {
805
0
        OGRLayer *poLayer =
806
0
            poSrcDS->ExecuteSQL(psOptions->osSQL.c_str(),
807
0
                                psOptions->poSpatialFilter.get(), nullptr);
808
0
        if (poLayer == nullptr)
809
0
        {
810
0
            return nullptr;
811
0
        }
812
813
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
814
0
            pScaledProgressArg(
815
0
                GDALCreateScaledProgress(0.0, bCloseReportsProgress ? 0.5 : 1.0,
816
0
                                         psOptions->pfnProgress,
817
0
                                         psOptions->pProgressData),
818
0
                GDALDestroyScaledProgress);
819
820
        // Custom layer will be rasterized in the first band.
821
0
        eErr = ProcessLayer(
822
0
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
823
0
            nYSize, 1, bIsXExtentSet, bIsYExtentSet, dfXMin, dfXMax, dfYMin,
824
0
            dfYMax, psOptions->osBurnAttribute, psOptions->dfIncreaseBurnValue,
825
0
            psOptions->dfMultiplyBurnValue, psOptions->eOutputType,
826
0
            psOptions->eAlgorithm, psOptions->pOptions.get(), psOptions->bQuiet,
827
0
            GDALScaledProgress, pScaledProgressArg.get());
828
829
0
        poSrcDS->ReleaseResultSet(poLayer);
830
0
    }
831
832
    /* -------------------------------------------------------------------- */
833
    /*      Process each layer.                                             */
834
    /* -------------------------------------------------------------------- */
835
0
    std::string osOutputSRS(psOptions->osOutputSRS);
836
0
    for (int i = 0; i < nLayerCount; i++)
837
0
    {
838
0
        auto poLayer = psOptions->aosLayers.empty()
839
0
                           ? poSrcDS->GetLayer(0)
840
0
                           : poSrcDS->GetLayerByName(psOptions->aosLayers[i]);
841
0
        if (!poLayer)
842
0
        {
843
0
            CPLError(CE_Failure, CPLE_AppDefined,
844
0
                     "Unable to find layer \"%s\".",
845
0
                     !psOptions->aosLayers.empty() && psOptions->aosLayers[i]
846
0
                         ? psOptions->aosLayers[i]
847
0
                         : "null");
848
0
            eErr = CE_Failure;
849
0
            break;
850
0
        }
851
852
0
        if (!psOptions->osWHERE.empty())
853
0
        {
854
0
            if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
855
0
                OGRERR_NONE)
856
0
            {
857
0
                eErr = CE_Failure;
858
0
                break;
859
0
            }
860
0
        }
861
862
0
        if (psOptions->poSpatialFilter)
863
0
            poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
864
865
        // Fetch the first meaningful SRS definition
866
0
        if (osOutputSRS.empty())
867
0
        {
868
0
            auto poSRS = poLayer->GetSpatialRef();
869
0
            if (poSRS)
870
0
                osOutputSRS = poSRS->exportToWkt();
871
0
        }
872
873
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
874
0
            pScaledProgressArg(
875
0
                GDALCreateScaledProgress(0.0, bCloseReportsProgress ? 0.5 : 1.0,
876
0
                                         psOptions->pfnProgress,
877
0
                                         psOptions->pProgressData),
878
0
                GDALDestroyScaledProgress);
879
880
0
        eErr = ProcessLayer(
881
0
            poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
882
0
            nYSize, i + 1 + nBands - nLayerCount, bIsXExtentSet, bIsYExtentSet,
883
0
            dfXMin, dfXMax, dfYMin, dfYMax, psOptions->osBurnAttribute,
884
0
            psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
885
0
            psOptions->eOutputType, psOptions->eAlgorithm,
886
0
            psOptions->pOptions.get(), psOptions->bQuiet, GDALScaledProgress,
887
0
            pScaledProgressArg.get());
888
0
        if (eErr != CE_None)
889
0
            break;
890
0
    }
891
892
    /* -------------------------------------------------------------------- */
893
    /*      Apply geotransformation matrix.                                 */
894
    /* -------------------------------------------------------------------- */
895
0
    poDstDS->SetGeoTransform(
896
0
        GDALGeoTransform(dfXMin, (dfXMax - dfXMin) / nXSize, 0.0, dfYMin, 0.0,
897
0
                         (dfYMax - dfYMin) / nYSize));
898
899
    /* -------------------------------------------------------------------- */
900
    /*      Apply SRS definition if set.                                    */
901
    /* -------------------------------------------------------------------- */
902
0
    if (!osOutputSRS.empty())
903
0
    {
904
0
        poDstDS->SetProjection(osOutputSRS.c_str());
905
0
    }
906
907
    /* -------------------------------------------------------------------- */
908
    /*      End                                                             */
909
    /* -------------------------------------------------------------------- */
910
911
0
    if (eErr != CE_None)
912
0
    {
913
0
        return nullptr;
914
0
    }
915
916
0
    if (bCloseReportsProgress)
917
0
    {
918
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
919
0
            pScaledProgressArg(
920
0
                GDALCreateScaledProgress(0.5, 1.0, psOptions->pfnProgress,
921
0
                                         psOptions->pProgressData),
922
0
                GDALDestroyScaledProgress);
923
924
0
        const bool bCanReopenWithCurrentDescription =
925
0
            poDstDS->CanReopenWithCurrentDescription();
926
927
0
        eErr = poDstDS->Close(GDALScaledProgress, pScaledProgressArg.get());
928
0
        poDstDS.reset();
929
0
        if (eErr != CE_None)
930
0
            return nullptr;
931
932
0
        if (bCanReopenWithCurrentDescription)
933
0
        {
934
0
            {
935
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
936
0
                poDstDS.reset(GDALDataset::Open(pszDest,
937
0
                                                GDAL_OF_RASTER | GDAL_OF_UPDATE,
938
0
                                                nullptr, nullptr, nullptr));
939
0
            }
940
0
            if (!poDstDS)
941
0
                poDstDS.reset(GDALDataset::Open(
942
0
                    pszDest, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
943
0
                    nullptr, nullptr));
944
0
        }
945
0
        else
946
0
        {
947
0
            struct DummyDataset final : public GDALDataset
948
0
            {
949
0
                DummyDataset() = default;
950
0
            };
951
952
0
            poDstDS = std::make_unique<DummyDataset>();
953
0
        }
954
0
    }
955
956
0
    return GDALDataset::ToHandle(poDstDS.release());
957
0
}
958
959
/************************************************************************/
960
/*                      GDALGridOptionsGetParser()                      */
961
/************************************************************************/
962
963
/*! @cond Doxygen_Suppress */
964
965
static std::unique_ptr<GDALArgumentParser>
966
GDALGridOptionsGetParser(GDALGridOptions *psOptions,
967
                         GDALGridOptionsForBinary *psOptionsForBinary,
968
                         int nCountClipSrc)
969
0
{
970
0
    auto argParser = std::make_unique<GDALArgumentParser>(
971
0
        "gdal_grid", /* bForBinary=*/psOptionsForBinary != nullptr);
972
973
0
    argParser->add_description(
974
0
        _("Creates a regular grid (raster) from the scattered data read from a "
975
0
          "vector datasource."));
976
977
0
    argParser->add_epilog(_(
978
0
        "Available algorithms and parameters with their defaults:\n"
979
0
        "    Inverse distance to a power (default)\n"
980
0
        "        "
981
0
        "invdist:power=2.0:smoothing=0.0:radius1=0.0:radius2=0.0:angle=0.0:max_"
982
0
        "points=0:min_points=0:nodata=0.0\n"
983
0
        "    Inverse distance to a power with nearest neighbor search\n"
984
0
        "        "
985
0
        "invdistnn:power=2.0:radius=1.0:max_points=12:min_points=0:nodata=0\n"
986
0
        "    Moving average\n"
987
0
        "        "
988
0
        "average:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
989
0
        "    Nearest neighbor\n"
990
0
        "        nearest:radius1=0.0:radius2=0.0:angle=0.0:nodata=0.0\n"
991
0
        "    Various data metrics\n"
992
0
        "        <metric "
993
0
        "name>:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
994
0
        "        possible metrics are:\n"
995
0
        "            minimum\n"
996
0
        "            maximum\n"
997
0
        "            range\n"
998
0
        "            count\n"
999
0
        "            average_distance\n"
1000
0
        "            average_distance_pts\n"
1001
0
        "    Linear\n"
1002
0
        "        linear:radius=-1.0:nodata=0.0\n"
1003
0
        "\n"
1004
0
        "For more details, consult https://gdal.org/programs/gdal_grid.html"));
1005
1006
0
    argParser->add_quiet_argument(
1007
0
        psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
1008
1009
0
    argParser->add_output_format_argument(psOptions->osFormat);
1010
1011
0
    argParser->add_output_type_argument(psOptions->eOutputType);
1012
1013
0
    argParser->add_argument("-txe")
1014
0
        .metavar("<xmin> <xmax>")
1015
0
        .nargs(2)
1016
0
        .scan<'g', double>()
1017
0
        .help(_("Set georeferenced X extents of output file to be created."));
1018
1019
0
    argParser->add_argument("-tye")
1020
0
        .metavar("<ymin> <ymax>")
1021
0
        .nargs(2)
1022
0
        .scan<'g', double>()
1023
0
        .help(_("Set georeferenced Y extents of output file to be created."));
1024
1025
0
    argParser->add_argument("-outsize")
1026
0
        .metavar("<xsize> <ysize>")
1027
0
        .nargs(2)
1028
0
        .scan<'i', int>()
1029
0
        .help(_("Set the size of the output file."));
1030
1031
0
    argParser->add_argument("-tr")
1032
0
        .metavar("<xres> <yes>")
1033
0
        .nargs(2)
1034
0
        .scan<'g', double>()
1035
0
        .help(_("Set target resolution."));
1036
1037
0
    argParser->add_creation_options_argument(psOptions->aosCreateOptions);
1038
1039
0
    argParser->add_argument("-zfield")
1040
0
        .metavar("<field_name>")
1041
0
        .store_into(psOptions->osBurnAttribute)
1042
0
        .help(_("Field name from which to get Z values."));
1043
1044
0
    argParser->add_argument("-z_increase")
1045
0
        .metavar("<increase_value>")
1046
0
        .store_into(psOptions->dfIncreaseBurnValue)
1047
0
        .help(_("Addition to the attribute field on the features to be used to "
1048
0
                "get a Z value from."));
1049
1050
0
    argParser->add_argument("-z_multiply")
1051
0
        .metavar("<multiply_value>")
1052
0
        .store_into(psOptions->dfMultiplyBurnValue)
1053
0
        .help(_("Multiplication ratio for the Z field.."));
1054
1055
0
    argParser->add_argument("-where")
1056
0
        .metavar("<expression>")
1057
0
        .store_into(psOptions->osWHERE)
1058
0
        .help(_("Query expression to be applied to select features to process "
1059
0
                "from the input layer(s)."));
1060
1061
0
    argParser->add_argument("-l")
1062
0
        .metavar("<layer_name>")
1063
0
        .append()
1064
0
        .action([psOptions](const std::string &s)
1065
0
                { psOptions->aosLayers.AddString(s.c_str()); })
1066
0
        .help(_("Layer(s) from the datasource that will be used for input "
1067
0
                "features."));
1068
1069
0
    argParser->add_argument("-sql")
1070
0
        .metavar("<select_statement>")
1071
0
        .store_into(psOptions->osSQL)
1072
0
        .help(_("SQL statement to be evaluated to produce a layer of features "
1073
0
                "to be processed."));
1074
1075
0
    argParser->add_argument("-spat")
1076
0
        .metavar("<xmin> <ymin> <xmax> <ymax>")
1077
0
        .nargs(4)
1078
0
        .scan<'g', double>()
1079
0
        .help(_("The area of interest. Only features within the rectangle will "
1080
0
                "be reported."));
1081
1082
0
    argParser->add_argument("-clipsrc")
1083
0
        .nargs(nCountClipSrc)
1084
0
        .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>|spat_extent")
1085
0
        .help(_("Clip geometries (in source SRS)."));
1086
1087
0
    argParser->add_argument("-clipsrcsql")
1088
0
        .metavar("<sql_statement>")
1089
0
        .store_into(psOptions->osClipSrcSQL)
1090
0
        .help(_("Select desired geometries from the source clip datasource "
1091
0
                "using an SQL query."));
1092
1093
0
    argParser->add_argument("-clipsrclayer")
1094
0
        .metavar("<layername>")
1095
0
        .store_into(psOptions->osClipSrcLayer)
1096
0
        .help(_("Select the named layer from the source clip datasource."));
1097
1098
0
    argParser->add_argument("-clipsrcwhere")
1099
0
        .metavar("<expression>")
1100
0
        .store_into(psOptions->osClipSrcWhere)
1101
0
        .help(_("Restrict desired geometries from the source clip layer based "
1102
0
                "on an attribute query."));
1103
1104
0
    argParser->add_argument("-a_srs")
1105
0
        .metavar("<srs_def>")
1106
0
        .action(
1107
0
            [psOptions](const std::string &osOutputSRSDef)
1108
0
            {
1109
0
                OGRSpatialReference oOutputSRS;
1110
1111
0
                if (oOutputSRS.SetFromUserInput(osOutputSRSDef.c_str()) !=
1112
0
                    OGRERR_NONE)
1113
0
                {
1114
0
                    throw std::invalid_argument(
1115
0
                        std::string("Failed to process SRS definition: ")
1116
0
                            .append(osOutputSRSDef));
1117
0
                }
1118
1119
0
                char *pszWKT = nullptr;
1120
0
                oOutputSRS.exportToWkt(&pszWKT);
1121
0
                if (pszWKT)
1122
0
                    psOptions->osOutputSRS = pszWKT;
1123
0
                CPLFree(pszWKT);
1124
0
            })
1125
0
        .help(_("Assign an output SRS, but without reprojecting."));
1126
1127
0
    argParser->add_argument("-a")
1128
0
        .metavar("<algorithm>[[:<parameter1>=<value1>]...]")
1129
0
        .action(
1130
0
            [psOptions](const std::string &s)
1131
0
            {
1132
0
                const char *pszAlgorithm = s.c_str();
1133
0
                void *pOptions = nullptr;
1134
0
                if (GDALGridParseAlgorithmAndOptions(pszAlgorithm,
1135
0
                                                     &psOptions->eAlgorithm,
1136
0
                                                     &pOptions) != CE_None)
1137
0
                {
1138
0
                    throw std::invalid_argument(
1139
0
                        "Failed to process algorithm name and parameters");
1140
0
                }
1141
0
                psOptions->pOptions.reset(pOptions);
1142
1143
0
                const CPLStringList aosParams(
1144
0
                    CSLTokenizeString2(pszAlgorithm, ":", FALSE));
1145
0
                const char *pszNoDataValue = aosParams.FetchNameValue("nodata");
1146
0
                if (pszNoDataValue != nullptr)
1147
0
                {
1148
0
                    psOptions->bNoDataSet = true;
1149
0
                    psOptions->dfNoDataValue = CPLAtofM(pszNoDataValue);
1150
0
                }
1151
0
            })
1152
0
        .help(_("Set the interpolation algorithm or data metric name and "
1153
0
                "(optionally) its parameters."));
1154
1155
0
    if (psOptionsForBinary)
1156
0
    {
1157
0
        argParser->add_open_options_argument(
1158
0
            &(psOptionsForBinary->aosOpenOptions));
1159
0
    }
1160
1161
0
    if (psOptionsForBinary)
1162
0
    {
1163
0
        argParser->add_argument("src_dataset_name")
1164
0
            .metavar("<src_dataset_name>")
1165
0
            .store_into(psOptionsForBinary->osSource)
1166
0
            .help(_("Input dataset."));
1167
1168
0
        argParser->add_argument("dst_dataset_name")
1169
0
            .metavar("<dst_dataset_name>")
1170
0
            .store_into(psOptionsForBinary->osDest)
1171
0
            .help(_("Output dataset."));
1172
0
    }
1173
1174
0
    return argParser;
1175
0
}
1176
1177
/*! @endcond */
1178
1179
/************************************************************************/
1180
/*                       GDALGridGetParserUsage()                       */
1181
/************************************************************************/
1182
1183
std::string GDALGridGetParserUsage()
1184
0
{
1185
0
    try
1186
0
    {
1187
0
        GDALGridOptions sOptions;
1188
0
        GDALGridOptionsForBinary sOptionsForBinary;
1189
0
        auto argParser =
1190
0
            GDALGridOptionsGetParser(&sOptions, &sOptionsForBinary, 1);
1191
0
        return argParser->usage();
1192
0
    }
1193
0
    catch (const std::exception &err)
1194
0
    {
1195
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1196
0
                 err.what());
1197
0
        return std::string();
1198
0
    }
1199
0
}
1200
1201
/************************************************************************/
1202
/*                  CHECK_HAS_ENOUGH_ADDITIONAL_ARGS()                  */
1203
/************************************************************************/
1204
1205
#ifndef CheckHasEnoughAdditionalArgs_defined
1206
#define CheckHasEnoughAdditionalArgs_defined
1207
1208
static bool CheckHasEnoughAdditionalArgs(CSLConstList papszArgv, int i,
1209
                                         int nExtraArg, int nArgc)
1210
0
{
1211
0
    if (i + nExtraArg >= nArgc)
1212
0
    {
1213
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1214
0
                 "%s option requires %d argument%s", papszArgv[i], nExtraArg,
1215
0
                 nExtraArg == 1 ? "" : "s");
1216
0
        return false;
1217
0
    }
1218
0
    return true;
1219
0
}
1220
#endif
1221
1222
#define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg)                            \
1223
0
    if (!CheckHasEnoughAdditionalArgs(papszArgv, i, nExtraArg, nArgc))         \
1224
0
    {                                                                          \
1225
0
        return nullptr;                                                        \
1226
0
    }
1227
1228
/************************************************************************/
1229
/*                         GDALGridOptionsNew()                         */
1230
/************************************************************************/
1231
1232
/**
1233
 * Allocates a GDALGridOptions struct.
1234
 *
1235
 * @param papszArgv NULL terminated list of options (potentially including
1236
 * filename and open options too), or NULL. The accepted options are the ones of
1237
 * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
1238
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1239
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1240
 *                           GDALGridOptionsForBinaryNew() prior to this
1241
 * function. Will be filled with potentially present filename, open options,...
1242
 * @return pointer to the allocated GDALGridOptions struct. Must be freed with
1243
 * GDALGridOptionsFree().
1244
 *
1245
 * @since GDAL 2.1
1246
 */
1247
1248
GDALGridOptions *
1249
GDALGridOptionsNew(char **papszArgv,
1250
                   GDALGridOptionsForBinary *psOptionsForBinary)
1251
0
{
1252
0
    auto psOptions = std::make_unique<GDALGridOptions>();
1253
1254
    /* -------------------------------------------------------------------- */
1255
    /*      Pre-processing for custom syntax that ArgumentParser does not   */
1256
    /*      support.                                                        */
1257
    /* -------------------------------------------------------------------- */
1258
1259
0
    CPLStringList aosArgv;
1260
0
    const int nArgc = CSLCount(papszArgv);
1261
0
    int nCountClipSrc = 0;
1262
0
    for (int i = 0;
1263
0
         i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
1264
0
    {
1265
0
        if (EQUAL(papszArgv[i], "-clipsrc"))
1266
0
        {
1267
0
            if (nCountClipSrc)
1268
0
            {
1269
0
                CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
1270
0
                         papszArgv[i]);
1271
0
                return nullptr;
1272
0
            }
1273
            // argparse doesn't handle well variable number of values
1274
            // just before the positional arguments, so we have to detect
1275
            // it manually and set the correct number.
1276
0
            nCountClipSrc = 1;
1277
0
            CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
1278
0
            if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
1279
0
                i + 4 < nArgc)
1280
0
            {
1281
0
                nCountClipSrc = 4;
1282
0
            }
1283
1284
0
            for (int j = 0; j < 1 + nCountClipSrc; ++j)
1285
0
            {
1286
0
                aosArgv.AddString(papszArgv[i]);
1287
0
                ++i;
1288
0
            }
1289
0
            --i;
1290
0
        }
1291
1292
0
        else
1293
0
        {
1294
0
            aosArgv.AddString(papszArgv[i]);
1295
0
        }
1296
0
    }
1297
1298
0
    try
1299
0
    {
1300
0
        auto argParser = GDALGridOptionsGetParser(
1301
0
            psOptions.get(), psOptionsForBinary, nCountClipSrc);
1302
1303
0
        argParser->parse_args_without_binary_name(aosArgv.List());
1304
1305
0
        if (auto oTXE = argParser->present<std::vector<double>>("-txe"))
1306
0
        {
1307
0
            psOptions->dfXMin = (*oTXE)[0];
1308
0
            psOptions->dfXMax = (*oTXE)[1];
1309
0
            psOptions->bIsXExtentSet = true;
1310
0
        }
1311
1312
0
        if (auto oTYE = argParser->present<std::vector<double>>("-tye"))
1313
0
        {
1314
0
            psOptions->dfYMin = (*oTYE)[0];
1315
0
            psOptions->dfYMax = (*oTYE)[1];
1316
0
            psOptions->bIsYExtentSet = true;
1317
0
        }
1318
1319
0
        if (auto oOutsize = argParser->present<std::vector<int>>("-outsize"))
1320
0
        {
1321
0
            psOptions->nXSize = (*oOutsize)[0];
1322
0
            psOptions->nYSize = (*oOutsize)[1];
1323
0
        }
1324
1325
0
        if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
1326
0
        {
1327
0
            psOptions->dfXRes = (*adfTargetRes)[0];
1328
0
            psOptions->dfYRes = (*adfTargetRes)[1];
1329
0
            if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
1330
0
            {
1331
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1332
0
                         "Wrong value for -tr parameters.");
1333
0
                return nullptr;
1334
0
            }
1335
0
        }
1336
1337
0
        if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
1338
0
        {
1339
0
            const double dfMinX = (*oSpat)[0];
1340
0
            const double dfMinY = (*oSpat)[1];
1341
0
            const double dfMaxX = (*oSpat)[2];
1342
0
            const double dfMaxY = (*oSpat)[3];
1343
1344
0
            auto poPolygon =
1345
0
                std::make_unique<OGRPolygon>(dfMinX, dfMinY, dfMaxX, dfMaxY);
1346
0
            psOptions->poSpatialFilter = std::move(poPolygon);
1347
0
        }
1348
1349
0
        if (auto oClipSrc =
1350
0
                argParser->present<std::vector<std::string>>("-clipsrc"))
1351
0
        {
1352
0
            const std::string &osVal = (*oClipSrc)[0];
1353
1354
0
            psOptions->poClipSrc.reset();
1355
0
            psOptions->osClipSrcDS.clear();
1356
1357
0
            VSIStatBufL sStat;
1358
0
            psOptions->bClipSrc = true;
1359
0
            if (oClipSrc->size() == 4)
1360
0
            {
1361
0
                const double dfMinX = CPLAtofM((*oClipSrc)[0].c_str());
1362
0
                const double dfMinY = CPLAtofM((*oClipSrc)[1].c_str());
1363
0
                const double dfMaxX = CPLAtofM((*oClipSrc)[2].c_str());
1364
0
                const double dfMaxY = CPLAtofM((*oClipSrc)[3].c_str());
1365
1366
0
                OGRLinearRing oRing;
1367
1368
0
                oRing.addPoint(dfMinX, dfMinY);
1369
0
                oRing.addPoint(dfMinX, dfMaxY);
1370
0
                oRing.addPoint(dfMaxX, dfMaxY);
1371
0
                oRing.addPoint(dfMaxX, dfMinY);
1372
0
                oRing.addPoint(dfMinX, dfMinY);
1373
1374
0
                auto poPoly = std::make_unique<OGRPolygon>();
1375
0
                poPoly->addRing(&oRing);
1376
0
                psOptions->poClipSrc = std::move(poPoly);
1377
0
            }
1378
0
            else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
1379
0
                      STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
1380
0
                     VSIStatL(osVal.c_str(), &sStat) != 0)
1381
0
            {
1382
0
                psOptions->poClipSrc =
1383
0
                    OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr)
1384
0
                        .first;
1385
0
                if (psOptions->poClipSrc == nullptr)
1386
0
                {
1387
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1388
0
                             "Invalid geometry. Must be a valid POLYGON or "
1389
0
                             "MULTIPOLYGON WKT");
1390
0
                    return nullptr;
1391
0
                }
1392
0
            }
1393
0
            else if (EQUAL(osVal.c_str(), "spat_extent"))
1394
0
            {
1395
                // Nothing to do
1396
0
            }
1397
0
            else
1398
0
            {
1399
0
                psOptions->osClipSrcDS = osVal;
1400
0
            }
1401
0
        }
1402
1403
0
        if (psOptions->bClipSrc && !psOptions->osClipSrcDS.empty())
1404
0
        {
1405
0
            psOptions->poClipSrc = LoadGeometry(
1406
0
                psOptions->osClipSrcDS, psOptions->osClipSrcSQL,
1407
0
                psOptions->osClipSrcLayer, psOptions->osClipSrcWhere);
1408
0
            if (!psOptions->poClipSrc)
1409
0
            {
1410
0
                CPLError(CE_Failure, CPLE_AppDefined,
1411
0
                         "Cannot load source clip geometry.");
1412
0
                return nullptr;
1413
0
            }
1414
0
        }
1415
0
        else if (psOptions->bClipSrc && !psOptions->poClipSrc &&
1416
0
                 !psOptions->poSpatialFilter)
1417
0
        {
1418
0
            CPLError(CE_Failure, CPLE_AppDefined,
1419
0
                     "-clipsrc must be used with -spat option or \n"
1420
0
                     "a bounding box, WKT string or datasource must be "
1421
0
                     "specified.");
1422
0
            return nullptr;
1423
0
        }
1424
1425
0
        if (psOptions->poSpatialFilter)
1426
0
        {
1427
0
            if (psOptions->poClipSrc)
1428
0
            {
1429
0
                auto poTemp = std::unique_ptr<OGRGeometry>(
1430
0
                    psOptions->poSpatialFilter->Intersection(
1431
0
                        psOptions->poClipSrc.get()));
1432
0
                if (poTemp)
1433
0
                {
1434
0
                    psOptions->poSpatialFilter = std::move(poTemp);
1435
0
                }
1436
1437
0
                psOptions->poClipSrc.reset();
1438
0
            }
1439
0
        }
1440
0
        else
1441
0
        {
1442
0
            if (psOptions->poClipSrc)
1443
0
            {
1444
0
                psOptions->poSpatialFilter = std::move(psOptions->poClipSrc);
1445
0
            }
1446
0
        }
1447
1448
0
        if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0 &&
1449
0
            !(psOptions->bIsXExtentSet && psOptions->bIsYExtentSet))
1450
0
        {
1451
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1452
0
                     "-txe ad -tye arguments must be provided when "
1453
0
                     "resolution is provided.");
1454
0
            return nullptr;
1455
0
        }
1456
1457
0
        return psOptions.release();
1458
0
    }
1459
0
    catch (const std::exception &err)
1460
0
    {
1461
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
1462
0
        return nullptr;
1463
0
    }
1464
0
}
1465
1466
/************************************************************************/
1467
/*                        GDALGridOptionsFree()                         */
1468
/************************************************************************/
1469
1470
/**
1471
 * Frees the GDALGridOptions struct.
1472
 *
1473
 * @param psOptions the options struct for GDALGrid().
1474
 *
1475
 * @since GDAL 2.1
1476
 */
1477
1478
void GDALGridOptionsFree(GDALGridOptions *psOptions)
1479
0
{
1480
0
    delete psOptions;
1481
0
}
1482
1483
/************************************************************************/
1484
/*                     GDALGridOptionsSetProgress()                     */
1485
/************************************************************************/
1486
1487
/**
1488
 * Set a progress function.
1489
 *
1490
 * @param psOptions the options struct for GDALGrid().
1491
 * @param pfnProgress the progress callback.
1492
 * @param pProgressData the user data for the progress callback.
1493
 *
1494
 * @since GDAL 2.1
1495
 */
1496
1497
void GDALGridOptionsSetProgress(GDALGridOptions *psOptions,
1498
                                GDALProgressFunc pfnProgress,
1499
                                void *pProgressData)
1500
0
{
1501
0
    psOptions->pfnProgress = pfnProgress;
1502
0
    psOptions->pProgressData = pProgressData;
1503
0
    if (pfnProgress == GDALTermProgress)
1504
0
        psOptions->bQuiet = false;
1505
0
}
1506
1507
#undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS