Coverage Report

Created: 2026-09-14 06:50

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