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

OSGeo / gdal / 14833953358

05 May 2025 10:05AM UTC coverage: 70.823% (-0.02%) from 70.841%
14833953358

Pull #12272

github

web-flow
Merge 69232b0dc into 7dae6010b
Pull Request #12272: GDALAlgorithm: move common code dealing with overwriting in GDALAlgorithm::ProcessDatasetArg()

83 of 86 new or added lines in 9 files covered. (96.51%)

195 existing lines in 43 files now uncovered.

565030 of 797804 relevant lines covered (70.82%)

234985.58 hits per line

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

91.57
/apps/gdalalg_raster_footprint.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "raster footprint" 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_raster_footprint.h"
14

15
#include "cpl_conv.h"
16
#include "cpl_vsi_virtual.h"
17

18
#include "gdal_priv.h"
19
#include "gdal_utils.h"
20

21
//! @cond Doxygen_Suppress
22

23
#ifndef _
24
#define _(x) (x)
25
#endif
26

27
/************************************************************************/
28
/*      GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm()    */
29
/************************************************************************/
30

31
GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm()
36✔
32
    : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
36✔
33
{
34
    AddProgressArg();
36✔
35

36
    AddOpenOptionsArg(&m_openOptions);
36✔
37
    AddInputFormatsArg(&m_inputFormats)
36✔
38
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
72✔
39
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
36✔
40

41
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR);
36✔
42
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ false,
43
                       /* bGDALGAllowed = */ false)
36✔
44
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
45
                         {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
108✔
46
    AddArg("output-layer", 0, _("Output layer name"), &m_outputLayerName)
72✔
47
        .SetDefault(m_outputLayerName);
36✔
48
    AddCreationOptionsArg(&m_creationOptions);
36✔
49
    AddLayerCreationOptionsArg(&m_layerCreationOptions);
36✔
50
    AddAppendUpdateArg(&m_append);
36✔
51
    AddOverwriteArg(&m_overwrite);
36✔
52

53
    AddBandArg(&m_bands);
36✔
54
    AddArg("combine-bands", 0,
55
           _("Defines how the mask bands of the selected bands are combined to "
56
             "generate a single mask band, before being vectorized."),
57
           &m_combineBands)
72✔
58
        .SetChoices("union", "intersection")
36✔
59
        .SetDefault(m_combineBands);
36✔
60
    AddArg("overview", 0, _("Which overview level of source file must be used"),
61
           &m_overview)
72✔
62
        .SetMutualExclusionGroup("overview-srcnodata")
72✔
63
        .SetMinValueIncluded(0);
36✔
64
    AddArg("src-nodata", 0, _("Set nodata values for input bands."),
65
           &m_srcNoData)
72✔
66
        .SetMinCount(1)
36✔
67
        .SetRepeatedArgAllowed(false)
36✔
68
        .SetMutualExclusionGroup("overview-srcnodata");
36✔
69
    AddArg("coordinate-system", 0, _("Target coordinate system"),
70
           &m_coordinateSystem)
72✔
71
        .SetChoices("georeferenced", "pixel");
36✔
72
    AddArg("dst-crs", 0, _("Destination CRS"), &m_dstCrs)
72✔
73
        .SetIsCRSArg()
72✔
74
        .AddHiddenAlias("t_srs");
36✔
75
    AddArg("split-multipolygons", 0,
76
           _("Whether to split multipolygons as several features each with one "
77
             "single polygon"),
78
           &m_splitMultiPolygons);
36✔
79
    AddArg("convex-hull", 0,
80
           _("Whether to compute the convex hull of the footprint"),
81
           &m_convexHull);
36✔
82
    AddArg("densify-distance", 0,
83
           _("Maximum distance between 2 consecutive points of the output "
84
             "geometry."),
85
           &m_densifyVal)
72✔
86
        .SetMinValueExcluded(0);
36✔
87
    AddArg(
88
        "simplify-tolerance", 0,
89
        _("Tolerance used to merge consecutive points of the output geometry."),
90
        &m_simplifyVal)
72✔
91
        .SetMinValueExcluded(0);
36✔
92
    AddArg("min-ring-area", 0, _("Minimum value for the area of a ring"),
93
           &m_minRingArea)
72✔
94
        .SetMinValueIncluded(0);
36✔
95
    AddArg("max-points", 0,
96
           _("Maximum number of points of each output geometry"), &m_maxPoints)
72✔
97
        .SetDefault(m_maxPoints)
36✔
98
        .AddValidationAction(
99
            [this]()
7✔
100
            {
101
                if (m_maxPoints != "unlimited")
2✔
102
                {
103
                    char *endptr = nullptr;
2✔
104
                    const auto nVal =
105
                        std::strtoll(m_maxPoints.c_str(), &endptr, 10);
2✔
106
                    if (nVal < 4 ||
3✔
107
                        endptr != m_maxPoints.c_str() + m_maxPoints.size())
1✔
108
                    {
109
                        ReportError(
1✔
110
                            CE_Failure, CPLE_IllegalArg,
111
                            "Value of 'max-points' should be a positive "
112
                            "integer greater or equal to 4, or 'unlimited'");
113
                        return false;
1✔
114
                    }
115
                }
116
                return true;
1✔
117
            });
36✔
118
    AddArg("location-field", 0,
119
           _("Name of the field where the path of the input dataset will be "
120
             "stored."),
121
           &m_locationField)
72✔
122
        .SetDefault(m_locationField)
36✔
123
        .SetMutualExclusionGroup("location");
36✔
124
    AddArg("no-location-field", 0,
125
           _("Disable creating a field with the path of the input dataset"),
126
           &m_noLocation)
72✔
127
        .SetMutualExclusionGroup("location");
36✔
128
    AddArg("absolute-path", 0,
129
           _("Whether the path to the input dataset should be stored as an "
130
             "absolute path"),
131
           &m_writeAbsolutePaths);
36✔
132

133
    AddValidationAction(
36✔
134
        [this]
69✔
135
        {
136
            if (auto poSrcDS = m_inputDataset.GetDatasetRef())
32✔
137
            {
138
                const int nOvrCount =
139
                    poSrcDS->GetRasterBand(1)->GetOverviewCount();
32✔
140
                if (m_overview >= 0 && poSrcDS->GetRasterCount() > 0 &&
35✔
141
                    m_overview >= nOvrCount)
3✔
142
                {
143
                    if (nOvrCount == 0)
2✔
144
                    {
145
                        ReportError(
1✔
146
                            CE_Failure, CPLE_IllegalArg,
147
                            "Source dataset has no overviews. "
148
                            "Argument 'overview' should not be specified.");
149
                    }
150
                    else
151
                    {
152
                        ReportError(
1✔
153
                            CE_Failure, CPLE_IllegalArg,
154
                            "Source dataset has only %d overview levels. "
155
                            "'overview' "
156
                            "value should be strictly lower than this number.",
157
                            nOvrCount);
158
                    }
159
                    return false;
2✔
160
                }
161
            }
162
            return true;
30✔
163
        });
164
}
36✔
165

166
/************************************************************************/
167
/*                 GDALRasterFootprintAlgorithm::RunImpl()              */
168
/************************************************************************/
169

170
bool GDALRasterFootprintAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
29✔
171
                                           void *pProgressData)
172
{
173
    GDALDatasetH hDstDS =
174
        GDALDataset::ToHandle(m_outputDataset.GetDatasetRef());
29✔
175

176
    const bool dstDSWasNull{!hDstDS};
29✔
177

178
    if (!hDstDS && !m_outputDataset.GetName().empty())
29✔
179
    {
180
        VSIStatBufL sStat;
181
        bool fileExists{VSIStatL(m_outputDataset.GetName().c_str(), &sStat) ==
3✔
182
                        0};
3✔
183

184
        {
185
            CPLErrorStateBackuper oCPLErrorHandlerPusher(CPLQuietErrorHandler);
6✔
186
            hDstDS = GDALOpenEx(m_outputDataset.GetName().c_str(),
3✔
187
                                GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR |
188
                                    GDAL_OF_UPDATE,
189
                                nullptr, nullptr, nullptr);
190
            CPLErrorReset();
3✔
191
        }
192

193
        if ((hDstDS || fileExists) && !m_overwrite && !m_append)
3✔
194
        {
UNCOV
195
            CPLError(CE_Failure, CPLE_AppDefined,
×
196
                     "Dataset '%s' already exists. Specify the --overwrite "
197
                     "option to overwrite it or the --append option to "
198
                     "append to it.",
UNCOV
199
                     m_outputDataset.GetName().c_str());
×
UNCOV
200
            GDALClose(hDstDS);
×
UNCOV
201
            return false;
×
202
        }
203

204
        if (hDstDS && fileExists && m_overwrite)
3✔
205
        {
206
            // Delete the existing file
UNCOV
207
            GDALClose(hDstDS);
×
UNCOV
208
            hDstDS = nullptr;
×
UNCOV
209
            if (VSIUnlink(m_outputDataset.GetName().c_str()) != 0)
×
210
            {
211
                CPLError(CE_Failure, CPLE_AppDefined,
×
212
                         "Failed to delete existing dataset '%s'.",
213
                         m_outputDataset.GetName().c_str());
×
214
                return false;
×
215
            }
216
        }
217
    }
218

219
    CPLStringList aosOptions;
58✔
220
    for (int band : m_bands)
35✔
221
    {
222
        aosOptions.push_back("-b");
6✔
223
        aosOptions.push_back(CPLSPrintf("%d", band));
6✔
224
    }
225

226
    aosOptions.push_back("-combine_bands");
29✔
227
    aosOptions.push_back(m_combineBands);
29✔
228

229
    if (m_overview >= 0)
29✔
230
    {
231
        aosOptions.push_back("-ovr");
1✔
232
        aosOptions.push_back(CPLSPrintf("%d", m_overview));
1✔
233
    }
234

235
    if (!m_srcNoData.empty())
29✔
236
    {
237
        aosOptions.push_back("-srcnodata");
2✔
238
        std::string s;
4✔
239
        for (double v : m_srcNoData)
5✔
240
        {
241
            if (!s.empty())
3✔
242
                s += " ";
1✔
243
            s += CPLSPrintf("%.17g", v);
3✔
244
        }
245
        aosOptions.push_back(s);
2✔
246
    }
247

248
    if (m_coordinateSystem == "pixel")
29✔
249
    {
250
        aosOptions.push_back("-t_cs");
1✔
251
        aosOptions.push_back("pixel");
1✔
252
    }
253
    else if (m_coordinateSystem == "georeferenced")
28✔
254
    {
255
        aosOptions.push_back("-t_cs");
1✔
256
        aosOptions.push_back("georef");
1✔
257
    }
258

259
    if (!m_dstCrs.empty())
29✔
260
    {
261
        aosOptions.push_back("-t_srs");
1✔
262
        aosOptions.push_back(m_dstCrs);
1✔
263
    }
264

265
    if (!m_format.empty())
29✔
266
    {
267
        aosOptions.push_back("-of");
25✔
268
        aosOptions.push_back(m_format.c_str());
25✔
269
    }
270

271
    for (const auto &co : m_creationOptions)
30✔
272
    {
273
        aosOptions.push_back("-dsco");
1✔
274
        aosOptions.push_back(co.c_str());
1✔
275
    }
276

277
    for (const auto &co : m_layerCreationOptions)
30✔
278
    {
279
        aosOptions.push_back("-lco");
1✔
280
        aosOptions.push_back(co.c_str());
1✔
281
    }
282

283
    if (GetArg("output-layer")->IsExplicitlySet())
29✔
284
    {
285
        aosOptions.push_back("-lyr_name");
5✔
286
        aosOptions.push_back(m_outputLayerName.c_str());
5✔
287
    }
288

289
    if (m_splitMultiPolygons)
29✔
290
        aosOptions.push_back("-split_polys");
1✔
291

292
    if (m_convexHull)
29✔
293
        aosOptions.push_back("-convex_hull");
1✔
294

295
    if (m_densifyVal > 0)
29✔
296
    {
297
        aosOptions.push_back("-densify");
1✔
298
        aosOptions.push_back(CPLSPrintf("%.17g", m_densifyVal));
1✔
299
    }
300

301
    if (m_simplifyVal > 0)
29✔
302
    {
303
        aosOptions.push_back("-simplify");
1✔
304
        aosOptions.push_back(CPLSPrintf("%.17g", m_simplifyVal));
1✔
305
    }
306

307
    aosOptions.push_back("-min_ring_area");
29✔
308
    aosOptions.push_back(CPLSPrintf("%.17g", m_minRingArea));
29✔
309

310
    aosOptions.push_back("-max_points");
29✔
311
    aosOptions.push_back(m_maxPoints);
29✔
312

313
    if (m_noLocation)
29✔
314
    {
315
        aosOptions.push_back("-no_location");
1✔
316
    }
317
    else
318
    {
319
        aosOptions.push_back("-location_field_name");
28✔
320
        aosOptions.push_back(m_locationField);
28✔
321

322
        if (m_writeAbsolutePaths)
28✔
323
            aosOptions.push_back("-write_absolute_path");
1✔
324
    }
325

326
    std::unique_ptr<GDALFootprintOptions, decltype(&GDALFootprintOptionsFree)>
327
        psOptions{GDALFootprintOptionsNew(aosOptions.List(), nullptr),
328
                  GDALFootprintOptionsFree};
58✔
329
    if (!psOptions)
29✔
330
        return false;
×
331

332
    GDALFootprintOptionsSetProgress(psOptions.get(), pfnProgress,
29✔
333
                                    pProgressData);
334

335
    GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
29✔
336
    auto poRetDS = GDALDataset::FromHandle(
29✔
337
        GDALFootprint(m_outputDataset.GetName().c_str(), hDstDS, hSrcDS,
29✔
338
                      psOptions.get(), nullptr));
29✔
339
    if (!poRetDS)
29✔
340
        return false;
1✔
341

342
    if (!hDstDS)
28✔
343
    {
344
        m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
27✔
345
    }
346
    else if (dstDSWasNull)
1✔
347
    {
348
        if (GDALClose(hDstDS) != CE_None)
×
349
        {
350
            CPLError(CE_Failure, CPLE_AppDefined,
×
351
                     "Failed to close output dataset");
352
            return false;
×
353
        }
354
    }
355

356
    return true;
28✔
357
}
358

359
//! @endcond
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc