Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_grid.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "vector grid" subcommand
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_vector_grid.h"
14
#include "gdalalg_vector_grid_average.h"
15
#include "gdalalg_vector_grid_data_metrics.h"
16
#include "gdalalg_vector_grid_invdist.h"
17
#include "gdalalg_vector_grid_invdistnn.h"
18
#include "gdalalg_vector_grid_linear.h"
19
#include "gdalalg_vector_grid_nearest.h"
20
21
#include "cpl_conv.h"
22
#include "gdal_priv.h"
23
#include "gdal_utils.h"
24
#include "ogrsf_frmts.h"
25
26
//! @cond Doxygen_Suppress
27
28
#ifndef _
29
0
#define _(x) (x)
30
#endif
31
32
/************************************************************************/
33
/*          GDALVectorGridAlgorithm::GDALVectorGridAlgorithm()          */
34
/************************************************************************/
35
36
GDALVectorGridAlgorithm::GDALVectorGridAlgorithm(bool standaloneStep)
37
0
    : GDALPipelineStepAlgorithm(
38
0
          NAME, DESCRIPTION, HELP_URL,
39
0
          ConstructorOptions()
40
0
              .SetStandaloneStep(standaloneStep)
41
0
              .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
42
0
{
43
0
    if (standaloneStep)
44
0
    {
45
0
        RegisterSubAlgorithm<GDALVectorGridAverageAlgorithmStandalone>();
46
0
        RegisterSubAlgorithm<GDALVectorGridInvdistAlgorithmStandalone>();
47
0
        RegisterSubAlgorithm<GDALVectorGridInvdistNNAlgorithmStandalone>();
48
0
        RegisterSubAlgorithm<GDALVectorGridLinearAlgorithmStandalone>();
49
0
        RegisterSubAlgorithm<GDALVectorGridNearestAlgorithmStandalone>();
50
0
        RegisterSubAlgorithm<GDALVectorGridMinimumAlgorithmStandalone>();
51
0
        RegisterSubAlgorithm<GDALVectorGridMaximumAlgorithmStandalone>();
52
0
        RegisterSubAlgorithm<GDALVectorGridRangeAlgorithmStandalone>();
53
0
        RegisterSubAlgorithm<GDALVectorGridCountAlgorithmStandalone>();
54
0
        RegisterSubAlgorithm<
55
0
            GDALVectorGridAverageDistanceAlgorithmStandalone>();
56
0
        RegisterSubAlgorithm<
57
0
            GDALVectorGridAverageDistancePointsAlgorithmStandalone>();
58
0
    }
59
0
    else
60
0
    {
61
0
        RegisterSubAlgorithm<GDALVectorGridAverageAlgorithm>();
62
0
        RegisterSubAlgorithm<GDALVectorGridInvdistAlgorithm>();
63
0
        RegisterSubAlgorithm<GDALVectorGridInvdistNNAlgorithm>();
64
0
        RegisterSubAlgorithm<GDALVectorGridLinearAlgorithm>();
65
0
        RegisterSubAlgorithm<GDALVectorGridNearestAlgorithm>();
66
0
        RegisterSubAlgorithm<GDALVectorGridMinimumAlgorithm>();
67
0
        RegisterSubAlgorithm<GDALVectorGridMaximumAlgorithm>();
68
0
        RegisterSubAlgorithm<GDALVectorGridRangeAlgorithm>();
69
0
        RegisterSubAlgorithm<GDALVectorGridCountAlgorithm>();
70
0
        RegisterSubAlgorithm<GDALVectorGridAverageDistanceAlgorithm>();
71
0
        RegisterSubAlgorithm<GDALVectorGridAverageDistancePointsAlgorithm>();
72
0
    }
73
0
}
74
75
/************************************************************************/
76
/*                  GDALVectorGridAlgorithm::RunStep()                  */
77
/************************************************************************/
78
79
bool GDALVectorGridAlgorithm::RunStep(GDALPipelineStepRunContext &)
80
0
{
81
0
    CPLError(CE_Failure, CPLE_AppDefined,
82
0
             "The Run() method should not be called directly on the \"gdal "
83
0
             "vector grid\" program.");
84
0
    return false;
85
0
}
86
87
/************************************************************************/
88
/*                  GDALVectorGeomAlgorithm::RunImpl()                  */
89
/************************************************************************/
90
91
bool GDALVectorGridAlgorithm::RunImpl(GDALProgressFunc, void *)
92
0
{
93
0
    CPLError(CE_Failure, CPLE_AppDefined,
94
0
             "The Run() method should not be called directly on the \"gdal "
95
0
             "vector grid\" program.");
96
0
    return false;
97
0
}
98
99
/************************************************************************/
100
/*                  GDALVectorGridAbstractAlgorithm()                   */
101
/************************************************************************/
102
103
GDALVectorGridAbstractAlgorithm::GDALVectorGridAbstractAlgorithm(
104
    const std::string &name, const std::string &description,
105
    const std::string &helpURL, bool standaloneStep)
106
0
    : GDALPipelineStepAlgorithm(
107
0
          name, description, helpURL,
108
0
          ConstructorOptions()
109
0
              .SetStandaloneStep(standaloneStep)
110
0
              .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
111
0
{
112
0
    AddProgressArg();
113
0
    if (standaloneStep)
114
0
    {
115
0
        AddOutputFormatArg(&m_format).AddMetadataItem(
116
0
            GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER, GDAL_DCAP_CREATE});
117
0
        AddOpenOptionsArg(&m_openOptions);
118
0
        AddInputFormatsArg(&m_inputFormats)
119
0
            .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
120
0
        AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR);
121
0
        AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
122
0
        AddCreationOptionsArg(&m_creationOptions);
123
0
    }
124
0
    else
125
0
    {
126
0
        AddVectorHiddenInputDatasetArg();
127
0
    }
128
0
    AddArg("extent", 0, _("Set the target georeferenced extent"),
129
0
           &m_targetExtent)
130
0
        .SetMinCount(4)
131
0
        .SetMaxCount(4)
132
0
        .SetRepeatedArgAllowed(false)
133
0
        .SetMetaVar("<xmin>,<ymin>,<xmax>,<ymax>");
134
0
    AddArg("resolution", 0, _("Set the target resolution"), &m_targetResolution)
135
0
        .SetMinCount(2)
136
0
        .SetMaxCount(2)
137
0
        .SetRepeatedArgAllowed(false)
138
0
        .SetMetaVar("<xres>,<yres>")
139
0
        .SetMutualExclusionGroup("size-or-resolution");
140
0
    AddArg("size", 0, _("Set the target size in pixels and lines"),
141
0
           &m_targetSize)
142
0
        .SetMinCount(2)
143
0
        .SetMaxCount(2)
144
0
        .SetRepeatedArgAllowed(false)
145
0
        .SetMetaVar("<xsize>,<ysize>")
146
0
        .SetMutualExclusionGroup("size-or-resolution");
147
0
    AddOutputDataTypeArg(&m_outputType).SetDefault(m_outputType);
148
0
    AddArg("crs", 0, _("Override the projection for the output file"), &m_crs)
149
0
        .AddHiddenAlias("srs")
150
0
        .SetIsCRSArg(/*noneAllowed=*/false);
151
0
    AddOverwriteArg(&m_overwrite);
152
0
    AddLayerNameArg(&m_layers)
153
0
        .SetMutualExclusionGroup("layer-sql")
154
0
        .AddAlias("layer");
155
0
    AddArg("sql", 0, _("SQL statement"), &m_sql)
156
0
        .SetReadFromFileAtSyntaxAllowed()
157
0
        .SetMetaVar("<statement>|@<filename>")
158
0
        .SetRemoveSQLCommentsEnabled()
159
0
        .SetMutualExclusionGroup("layer-sql");
160
0
    AddBBOXArg(
161
0
        &m_bbox,
162
0
        _("Select only points contained within the specified bounding box"));
163
0
    AddArg("zfield", 0, _("Field name from which to get Z values."), &m_zField)
164
0
        .AddHiddenAlias("z-field");
165
0
    AddArg("zoffset", 0,
166
0
           _("Value to add to the Z field value (applied before zmultiply)"),
167
0
           &m_zOffset)
168
0
        .SetDefault(m_zOffset)
169
0
        .AddHiddenAlias("z-offset");
170
0
    AddArg("zmultiply", 0,
171
0
           _("Multiplication factor for the Z field value (applied after "
172
0
             "zoffset)"),
173
0
           &m_zMultiply)
174
0
        .SetDefault(m_zMultiply)
175
0
        .AddHiddenAlias("z-multiply");
176
177
0
    AddValidationAction(
178
0
        [this]()
179
0
        {
180
0
            bool ret = true;
181
0
            if (!m_targetResolution.empty() && m_targetExtent.empty())
182
0
            {
183
0
                ReportError(CE_Failure, CPLE_AppDefined,
184
0
                            "'resolution' should be defined when 'extent' is.");
185
0
                ret = false;
186
0
            }
187
0
            return ret;
188
0
        });
189
0
}
190
191
/************************************************************************/
192
/*              GDALVectorGridAbstractAlgorithm::RunStep()              */
193
/************************************************************************/
194
195
bool GDALVectorGridAbstractAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
196
0
{
197
0
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
198
0
    CPLAssert(poSrcDS);
199
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
200
201
0
    CPLStringList aosOptions;
202
203
0
    std::string outputFilename;
204
0
    if (m_standaloneStep)
205
0
    {
206
0
        outputFilename = m_outputDataset.GetName();
207
0
        if (!m_format.empty())
208
0
        {
209
0
            aosOptions.AddString("-of");
210
0
            aosOptions.AddString(m_format.c_str());
211
0
        }
212
213
0
        for (const std::string &co : m_creationOptions)
214
0
        {
215
0
            aosOptions.AddString("-co");
216
0
            aosOptions.AddString(co.c_str());
217
0
        }
218
0
    }
219
0
    else
220
0
    {
221
0
        outputFilename = CPLGenerateTempFilenameSafe("_grid.tif");
222
223
0
        aosOptions.AddString("-of");
224
0
        aosOptions.AddString("GTiff");
225
226
0
        aosOptions.AddString("-co");
227
0
        aosOptions.AddString("TILED=YES");
228
0
    }
229
230
0
    if (!m_targetExtent.empty())
231
0
    {
232
0
        aosOptions.AddString("-txe");
233
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[0]));
234
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[2]));
235
0
        aosOptions.AddString("-tye");
236
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[1]));
237
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[3]));
238
0
    }
239
240
0
    if (!m_bbox.empty())
241
0
    {
242
0
        aosOptions.AddString("-clipsrc");
243
0
        for (double v : m_bbox)
244
0
        {
245
0
            aosOptions.AddString(CPLSPrintf("%.17g", v));
246
0
        }
247
0
    }
248
249
0
    if (!m_targetResolution.empty())
250
0
    {
251
0
        aosOptions.AddString("-tr");
252
0
        for (double targetResolution : m_targetResolution)
253
0
        {
254
0
            aosOptions.AddString(CPLSPrintf("%.17g", targetResolution));
255
0
        }
256
0
    }
257
258
0
    if (!m_targetSize.empty())
259
0
    {
260
0
        aosOptions.AddString("-outsize");
261
0
        for (int targetSize : m_targetSize)
262
0
        {
263
0
            aosOptions.AddString(CPLSPrintf("%d", targetSize));
264
0
        }
265
0
    }
266
267
0
    if (!m_outputType.empty())
268
0
    {
269
0
        aosOptions.AddString("-ot");
270
0
        aosOptions.AddString(m_outputType.c_str());
271
0
    }
272
273
0
    if (!m_crs.empty())
274
0
    {
275
0
        aosOptions.AddString("-a_srs");
276
0
        aosOptions.AddString(m_crs.c_str());
277
0
    }
278
279
0
    if (m_sql.empty())
280
0
    {
281
0
        for (const std::string &layer : m_layers)
282
0
        {
283
0
            aosOptions.AddString("-l");
284
0
            aosOptions.AddString(layer.c_str());
285
0
        }
286
0
    }
287
0
    else
288
0
    {
289
0
        aosOptions.AddString("-sql");
290
0
        aosOptions.AddString(m_sql.c_str());
291
0
    }
292
293
0
    if (m_zOffset != 0)
294
0
    {
295
0
        aosOptions.AddString("-z_increase");
296
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_zOffset));
297
0
    }
298
299
0
    if (m_zMultiply != 0)
300
0
    {
301
0
        aosOptions.AddString("-z_multiply");
302
0
        aosOptions.AddString(CPLSPrintf("%.17g", m_zMultiply));
303
0
    }
304
305
0
    if (!m_zField.empty())
306
0
    {
307
0
        aosOptions.AddString("-zfield");
308
0
        aosOptions.AddString(m_zField.c_str());
309
0
    }
310
0
    else if (m_sql.empty())
311
0
    {
312
0
        const auto CheckLayer = [this](OGRLayer *poLayer)
313
0
        {
314
0
            auto poFeat =
315
0
                std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
316
0
            poLayer->ResetReading();
317
0
            if (poFeat)
318
0
            {
319
0
                const auto poGeom = poFeat->GetGeometryRef();
320
0
                if (!poGeom || !poGeom->Is3D())
321
0
                {
322
0
                    ReportError(
323
0
                        CE_Warning, CPLE_AppDefined,
324
0
                        "At least one geometry of layer '%s' lacks a Z "
325
0
                        "component. You may need to set the 'zfield' argument",
326
0
                        poLayer->GetName());
327
0
                    return false;
328
0
                }
329
0
            }
330
0
            return true;
331
0
        };
332
333
0
        if (m_layers.empty())
334
0
        {
335
0
            for (auto poLayer : poSrcDS->GetLayers())
336
0
            {
337
0
                if (!CheckLayer(poLayer))
338
0
                    break;
339
0
            }
340
0
        }
341
0
        else
342
0
        {
343
0
            for (const std::string &layerName : m_layers)
344
0
            {
345
0
                auto poLayer = poSrcDS->GetLayerByName(layerName.c_str());
346
0
                if (poLayer && !CheckLayer(poLayer))
347
0
                    break;
348
0
            }
349
0
        }
350
0
    }
351
352
0
    aosOptions.AddString("-a");
353
0
    aosOptions.AddString(GetGridAlgorithm().c_str());
354
355
0
    bool bOK = false;
356
0
    std::unique_ptr<GDALGridOptions, decltype(&GDALGridOptionsFree)> psOptions{
357
0
        GDALGridOptionsNew(aosOptions.List(), nullptr), GDALGridOptionsFree};
358
359
0
    if (psOptions)
360
0
    {
361
0
        GDALGridOptionsSetProgress(psOptions.get(), ctxt.m_pfnProgress,
362
0
                                   ctxt.m_pProgressData);
363
364
0
        auto poRetDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
365
0
            GDALGrid(outputFilename.c_str(), GDALDataset::ToHandle(poSrcDS),
366
0
                     psOptions.get(), nullptr)));
367
368
0
        if (poRetDS)
369
0
        {
370
0
            bOK = true;
371
372
0
            if (!m_standaloneStep)
373
0
            {
374
0
                VSIUnlink(outputFilename.c_str());
375
0
                poRetDS->MarkSuppressOnClose();
376
0
            }
377
378
0
            m_outputDataset.Set(std::move(poRetDS));
379
0
        }
380
0
    }
381
382
0
    return bOK;
383
0
}
384
385
/************************************************************************/
386
/*              GDALVectorGridAbstractAlgorithm::RunImpl()              */
387
/************************************************************************/
388
389
bool GDALVectorGridAbstractAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
390
                                              void *pProgressData)
391
0
{
392
0
    GDALPipelineStepRunContext stepCtxt;
393
0
    stepCtxt.m_pfnProgress = pfnProgress;
394
0
    stepCtxt.m_pProgressData = pProgressData;
395
0
    return RunPreStepPipelineValidations() && RunStep(stepCtxt);
396
0
}
397
398
/************************************************************************/
399
/*           GDALVectorGridAbstractAlgorithm::AddRadiusArg()            */
400
/************************************************************************/
401
402
GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddRadiusArg()
403
0
{
404
0
    return AddArg("radius", 0, _("Radius of the search circle"), &m_radius)
405
0
        .SetMutualExclusionGroup("radius");
406
0
}
407
408
/************************************************************************/
409
/*      GDALVectorGridAbstractAlgorithm::AddRadius1AndRadius2Arg()      */
410
/************************************************************************/
411
412
void GDALVectorGridAbstractAlgorithm::AddRadius1AndRadius2Arg()
413
0
{
414
0
    AddArg("radius1", 0, _("First axis of the search ellipse"), &m_radius1)
415
0
        .SetMutualExclusionGroup("radius");
416
0
    AddArg("radius2", 0, _("Second axis of the search ellipse"), &m_radius2);
417
418
0
    AddValidationAction(
419
0
        [this]()
420
0
        {
421
0
            bool ret = true;
422
0
            if (m_radius1 > 0 && m_radius2 == 0)
423
0
            {
424
0
                ReportError(CE_Failure, CPLE_AppDefined,
425
0
                            "'radius2' should be defined when 'radius1' is.");
426
0
                ret = false;
427
0
            }
428
0
            else if (m_radius2 > 0 && m_radius1 == 0)
429
0
            {
430
0
                ReportError(CE_Failure, CPLE_AppDefined,
431
0
                            "'radius1' should be defined when 'radius2' is.");
432
0
                ret = false;
433
0
            }
434
0
            return ret;
435
0
        });
436
0
}
437
438
/************************************************************************/
439
/*            GDALVectorGridAbstractAlgorithm::AddAngleArg()            */
440
/************************************************************************/
441
442
GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddAngleArg()
443
0
{
444
0
    return AddArg("angle", 0,
445
0
                  _("Angle of search ellipse rotation in degrees (counter "
446
0
                    "clockwise)"),
447
0
                  &m_angle)
448
0
        .SetDefault(m_angle);
449
0
}
450
451
/************************************************************************/
452
/*          GDALVectorGridAbstractAlgorithm::AddMinPointsArg()          */
453
/************************************************************************/
454
455
GDALInConstructionAlgorithmArg &
456
GDALVectorGridAbstractAlgorithm::AddMinPointsArg()
457
0
{
458
0
    return AddArg("min-points", 0, _("Minimum number of data points to use"),
459
0
                  &m_minPoints)
460
0
        .SetDefault(m_minPoints);
461
0
}
462
463
/************************************************************************/
464
/*          GDALVectorGridAbstractAlgorithm::AddMaxPointsArg()          */
465
/************************************************************************/
466
467
GDALInConstructionAlgorithmArg &
468
GDALVectorGridAbstractAlgorithm::AddMaxPointsArg()
469
0
{
470
0
    return AddArg("max-points", 0, _("Maximum number of data points to use"),
471
0
                  &m_maxPoints)
472
0
        .SetDefault(m_maxPoints);
473
0
}
474
475
/************************************************************************/
476
/*   GDALVectorGridAbstractAlgorithm::AddMinMaxPointsPerQuadrantArg()   */
477
/************************************************************************/
478
479
void GDALVectorGridAbstractAlgorithm::AddMinMaxPointsPerQuadrantArg()
480
0
{
481
0
    AddArg("min-points-per-quadrant", 0,
482
0
           _("Minimum number of data points to use per quadrant"),
483
0
           &m_minPointsPerQuadrant)
484
0
        .SetDefault(m_minPointsPerQuadrant);
485
0
    AddArg("max-points-per-quadrant", 0,
486
0
           _("Maximum number of data points to use per quadrant"),
487
0
           &m_maxPointsPerQuadrant)
488
0
        .SetDefault(m_maxPointsPerQuadrant);
489
0
}
490
491
/************************************************************************/
492
/*           GDALVectorGridAbstractAlgorithm::AddNodataArg()            */
493
/************************************************************************/
494
495
GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddNodataArg()
496
0
{
497
0
    return AddArg("nodata", 0, _("Target nodata value"), &m_nodata)
498
0
        .SetDefault(m_nodata);
499
0
}
500
501
0
GDALVectorGridAlgorithmStandalone::~GDALVectorGridAlgorithmStandalone() =
502
    default;
503
504
//! @endcond