Coverage Report

Created: 2026-04-10 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdal_footprint_lib.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Utilities
4
 * Purpose:  Footprint OGR shapes into a GDAL raster.
5
 * Authors:  Even Rouault, <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "gdal_utils.h"
15
#include "gdal_utils_priv.h"
16
17
#include <cmath>
18
#include <cstdio>
19
#include <cstdlib>
20
#include <cstring>
21
#include <algorithm>
22
#include <limits>
23
#include <vector>
24
25
#include "commonutils.h"
26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
#include "cpl_progress.h"
29
#include "cpl_string.h"
30
#include "cpl_vsi.h"
31
#include "gdal.h"
32
#include "gdal_alg.h"
33
#include "gdal_priv.h"
34
#include "gdal_maskbands.h"
35
#include "ogr_api.h"
36
#include "ogr_core.h"
37
#include "memdataset.h"
38
#include "ogrsf_frmts.h"
39
#include "ogr_spatialref.h"
40
#include "gdalargumentparser.h"
41
42
constexpr const char *DEFAULT_LAYER_NAME = "footprint";
43
44
/************************************************************************/
45
/*                         GDALFootprintOptions                         */
46
/************************************************************************/
47
48
struct GDALFootprintOptions
49
{
50
    /*! output format. Use the short format name. */
51
    std::string osFormat{};
52
53
    /*! the progress function to use */
54
    GDALProgressFunc pfnProgress = GDALDummyProgress;
55
56
    /*! pointer to the progress data variable */
57
    void *pProgressData = nullptr;
58
59
    bool bCreateOutput = false;
60
61
    std::string osDestLayerName{};
62
63
    /*! Layer creation options */
64
    CPLStringList aosLCO{};
65
66
    /*! Dataset creation options */
67
    CPLStringList aosDSCO{};
68
69
    /*! Overview index: 0 = first overview level */
70
    int nOvrIndex = -1;
71
72
    /** Whether output geometry should be in georeferenced coordinates, if
73
     * possible (if explicitly requested, bOutCSGeorefRequested is also set)
74
     * false = in pixel coordinates
75
     */
76
    bool bOutCSGeoref = true;
77
78
    /** Whether -t_cs georef has been explicitly set */
79
    bool bOutCSGeorefRequested = false;
80
81
    OGRSpatialReference oOutputSRS{};
82
83
    bool bSplitPolys = false;
84
85
    double dfDensifyDistance = 0;
86
87
    double dfSimplifyTolerance = 0;
88
89
    bool bConvexHull = false;
90
91
    double dfMinRingArea = 0;
92
93
    int nMaxPoints = 100;
94
95
    /*! Source bands to take into account */
96
    std::vector<int> anBands{};
97
98
    /*! Whether to combine bands unioning (true) or intersecting (false) */
99
    bool bCombineBandsUnion = true;
100
101
    /*! Field name where to write the path of the raster. Empty if not desired */
102
    std::string osLocationFieldName = "location";
103
104
    /*! Clears the osLocationFieldName var when set */
105
    bool bClearLocation = false;
106
107
    /*! Whether to force writing absolute paths in location field. */
108
    bool bAbsolutePath = false;
109
110
    std::string osSrcNoData{};
111
};
112
113
static std::unique_ptr<GDALArgumentParser> GDALFootprintAppOptionsGetParser(
114
    GDALFootprintOptions *psOptions,
115
    GDALFootprintOptionsForBinary *psOptionsForBinary)
116
0
{
117
0
    auto argParser = std::make_unique<GDALArgumentParser>(
118
0
        "gdal_footprint", /* bForBinary=*/psOptionsForBinary != nullptr);
119
120
0
    argParser->add_description(_("Compute footprint of a raster."));
121
122
0
    argParser->add_epilog(_("For more details, consult "
123
0
                            "https://gdal.org/programs/gdal_footprint.html"));
124
125
0
    argParser->add_argument("-b")
126
0
        .metavar("<band>")
127
0
        .scan<'i', int>()
128
0
        .append()
129
0
        .store_into(psOptions->anBands)
130
0
        .help(_("Band(s) of interest."));
131
132
0
    argParser->add_argument("-combine_bands")
133
0
        .choices("union", "intersection")
134
0
        .action([psOptions](const auto &s)
135
0
                { psOptions->bCombineBandsUnion = s == "union"; })
136
0
        .default_value("union")
137
0
        .help(_("Defines how the mask bands of the selected bands are combined "
138
0
                "to generate a single mask band, before being vectorized."));
139
140
0
    {
141
0
        auto &group = argParser->add_mutually_exclusive_group();
142
143
0
        group.add_argument("-ovr")
144
0
            .metavar("<index>")
145
0
            .scan<'i', int>()
146
0
            .store_into(psOptions->nOvrIndex)
147
0
            .help(_("Defines which overview level of source file must be used, "
148
0
                    "when overviews are available on the source raster."));
149
150
0
        group.add_argument("-srcnodata")
151
0
            .metavar("\"<value>[ <value>]...\"")
152
0
            .store_into(psOptions->osSrcNoData)
153
0
            .help(_("Set nodata value(s) for input bands."));
154
0
    }
155
156
0
    argParser->add_argument("-t_cs")
157
0
        .choices("pixel", "georef")
158
0
        .default_value("georef")
159
0
        .action(
160
0
            [psOptions](const auto &s)
161
0
            {
162
0
                const bool georefSet{s == "georef"};
163
0
                psOptions->bOutCSGeoref = georefSet;
164
0
                psOptions->bOutCSGeorefRequested = georefSet;
165
0
            })
166
0
        .help(_("Target coordinate system."));
167
168
    // Note: no store_into (requires post validation)
169
0
    argParser->add_argument("-t_srs")
170
0
        .metavar("<srs_def>")
171
0
        .help(_("Target CRS of the output file.."));
172
173
0
    argParser->add_argument("-split_polys")
174
0
        .flag()
175
0
        .store_into(psOptions->bSplitPolys)
176
0
        .help(_("Split multipolygons into several features each one with a "
177
0
                "single polygon."));
178
179
0
    argParser->add_argument("-convex_hull")
180
0
        .flag()
181
0
        .store_into(psOptions->bConvexHull)
182
0
        .help(_("Compute the convex hull of the (multi)polygons."));
183
184
0
    argParser->add_argument("-densify")
185
0
        .metavar("<value>")
186
0
        .scan<'g', double>()
187
0
        .store_into(psOptions->dfDensifyDistance)
188
0
        .help(_("The specified value of this option is the maximum distance "
189
0
                "between 2 consecutive points of the output geometry. "));
190
191
0
    argParser->add_argument("-simplify")
192
0
        .metavar("<value>")
193
0
        .scan<'g', double>()
194
0
        .store_into(psOptions->dfSimplifyTolerance)
195
0
        .help(_("The specified value of this option is the tolerance used to "
196
0
                "merge consecutive points of the output geometry."));
197
198
0
    argParser->add_argument("-min_ring_area")
199
0
        .metavar("<value>")
200
0
        .scan<'g', double>()
201
0
        .store_into(psOptions->dfMinRingArea)
202
0
        .help(_("The specified value of this option is the minimum area of a "
203
0
                "ring to be considered."));
204
205
    // Note: no store_into (requires post validation)
206
0
    argParser->add_argument("-max_points")
207
0
        .metavar("<value>|unlimited")
208
0
        .default_value("100")
209
0
        .help(_("The maximum number of points in the output geometry."));
210
211
0
    if (psOptionsForBinary)
212
0
    {
213
0
        argParser->add_quiet_argument(&psOptionsForBinary->bQuiet);
214
0
        argParser->add_open_options_argument(
215
0
            psOptionsForBinary->aosOpenOptions);
216
0
    }
217
218
0
    argParser->add_output_format_argument(psOptions->osFormat);
219
220
0
    {
221
0
        auto &group = argParser->add_mutually_exclusive_group();
222
223
0
        group.add_argument("-location_field_name")
224
0
            .metavar("<field_name>")
225
0
            .default_value("location")
226
0
            .store_into(psOptions->osLocationFieldName)
227
0
            .help(_(
228
0
                "Specifies the name of the field in the resulting vector "
229
0
                "dataset where the path of the input dataset will be stored."));
230
231
0
        group.add_argument("-no_location")
232
0
            .flag()
233
0
            .store_into(psOptions->bClearLocation)
234
0
            .help(
235
0
                _("Turns off the writing of the path of the input dataset as a "
236
0
                  "field in the output vector dataset."));
237
0
    }
238
239
0
    argParser->add_argument("-write_absolute_path")
240
0
        .flag()
241
0
        .store_into(psOptions->bAbsolutePath)
242
0
        .help(_("Enables writing the absolute path of the input dataset."));
243
244
0
    argParser->add_layer_creation_options_argument(psOptions->aosLCO);
245
246
0
    argParser->add_dataset_creation_options_argument(psOptions->aosDSCO);
247
248
0
    argParser->add_argument("-lyr_name")
249
0
        .metavar("<value>")
250
0
        .store_into(psOptions->osDestLayerName)
251
0
        .help(_("Name of the target layer."));
252
253
0
    if (psOptionsForBinary)
254
0
    {
255
0
        argParser->add_argument("-overwrite")
256
0
            .flag()
257
0
            .store_into(psOptionsForBinary->bOverwrite)
258
0
            .help(_("Overwrite the target layer if it exists."));
259
260
0
        argParser->add_argument("src_filename")
261
0
            .metavar("<src_filename>")
262
0
            .store_into(psOptionsForBinary->osSource)
263
0
            .help(_("Source raster file name."));
264
265
0
        argParser->add_argument("dst_filename")
266
0
            .metavar("<dst_filename>")
267
0
            .store_into(psOptionsForBinary->osDest)
268
0
            .help(_("Destination vector file name."));
269
0
    }
270
271
0
    return argParser;
272
0
}
273
274
/************************************************************************/
275
/*                        GDALFootprintMaskBand                         */
276
/************************************************************************/
277
278
class GDALFootprintMaskBand final : public GDALRasterBand
279
{
280
    GDALRasterBand *m_poSrcBand = nullptr;
281
282
    CPL_DISALLOW_COPY_ASSIGN(GDALFootprintMaskBand)
283
284
  public:
285
    explicit GDALFootprintMaskBand(GDALRasterBand *poSrcBand)
286
0
        : m_poSrcBand(poSrcBand)
287
0
    {
288
0
        nRasterXSize = m_poSrcBand->GetXSize();
289
0
        nRasterYSize = m_poSrcBand->GetYSize();
290
0
        eDataType = GDT_UInt8;
291
0
        m_poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
292
0
    }
293
294
  protected:
295
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
296
297
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
298
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
299
                     GDALDataType eBufType, GSpacing nPixelSpace,
300
                     GSpacing nLineSpace,
301
                     GDALRasterIOExtraArg *psExtraArg) override;
302
};
303
304
CPLErr GDALFootprintMaskBand::IReadBlock(int nBlockXOff, int nBlockYOff,
305
                                         void *pData)
306
0
{
307
0
    int nWindowXSize;
308
0
    int nWindowYSize;
309
0
    m_poSrcBand->GetActualBlockSize(nBlockXOff, nBlockYOff, &nWindowXSize,
310
0
                                    &nWindowYSize);
311
0
    GDALRasterIOExtraArg sExtraArg;
312
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
313
0
    return IRasterIO(GF_Read, nBlockXOff * nBlockXSize,
314
0
                     nBlockYOff * nBlockYSize, nWindowXSize, nWindowYSize,
315
0
                     pData, nWindowXSize, nWindowYSize, GDT_UInt8, 1,
316
0
                     nBlockXSize, &sExtraArg);
317
0
}
318
319
CPLErr GDALFootprintMaskBand::IRasterIO(
320
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
321
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
322
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
323
0
{
324
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
325
0
        eBufType == GDT_UInt8 && nPixelSpace == 1)
326
0
    {
327
        // Request when band seen as the mask band for GDALPolygonize()
328
329
0
        if (m_poSrcBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pData,
330
0
                                  nBufXSize, nBufYSize, eBufType, nPixelSpace,
331
0
                                  nLineSpace, psExtraArg) != CE_None)
332
0
        {
333
0
            return CE_Failure;
334
0
        }
335
0
        GByte *pabyData = static_cast<GByte *>(pData);
336
0
        for (int iY = 0; iY < nYSize; ++iY)
337
0
        {
338
0
            for (int iX = 0; iX < nXSize; ++iX)
339
0
            {
340
0
                if (pabyData[iX])
341
0
                    pabyData[iX] = 1;
342
0
            }
343
0
            pabyData += nLineSpace;
344
0
        }
345
346
0
        return CE_None;
347
0
    }
348
349
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
350
0
        eBufType == GDT_Int64 &&
351
0
        nPixelSpace == static_cast<int>(sizeof(int64_t)) &&
352
0
        (nLineSpace % nPixelSpace) == 0)
353
0
    {
354
        // Request when band seen as the value band for GDALPolygonize()
355
356
0
        if (m_poSrcBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pData,
357
0
                                  nBufXSize, nBufYSize, eBufType, nPixelSpace,
358
0
                                  nLineSpace, psExtraArg) != CE_None)
359
0
        {
360
0
            return CE_Failure;
361
0
        }
362
0
        int64_t *panData = static_cast<int64_t *>(pData);
363
0
        for (int iY = 0; iY < nYSize; ++iY)
364
0
        {
365
0
            for (int iX = 0; iX < nXSize; ++iX)
366
0
            {
367
0
                if (panData[iX])
368
0
                    panData[iX] = 1;
369
0
            }
370
0
            panData += (nLineSpace / nPixelSpace);
371
0
        }
372
373
0
        return CE_None;
374
0
    }
375
376
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
377
0
                                     pData, nBufXSize, nBufYSize, eBufType,
378
0
                                     nPixelSpace, nLineSpace, psExtraArg);
379
0
}
380
381
/************************************************************************/
382
/*                    GDALFootprintCombinedMaskBand                     */
383
/************************************************************************/
384
385
class GDALFootprintCombinedMaskBand final : public GDALRasterBand
386
{
387
    std::vector<GDALRasterBand *> m_apoSrcBands{};
388
389
    /** Whether to combine bands unioning (true) or intersecting (false) */
390
    bool m_bUnion = false;
391
392
  public:
393
    explicit GDALFootprintCombinedMaskBand(
394
        const std::vector<GDALRasterBand *> &apoSrcBands, bool bUnion)
395
0
        : m_apoSrcBands(apoSrcBands), m_bUnion(bUnion)
396
0
    {
397
0
        nRasterXSize = m_apoSrcBands[0]->GetXSize();
398
0
        nRasterYSize = m_apoSrcBands[0]->GetYSize();
399
0
        eDataType = GDT_UInt8;
400
0
        m_apoSrcBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
401
0
    }
402
403
  protected:
404
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
405
406
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
407
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
408
                     GDALDataType eBufType, GSpacing nPixelSpace,
409
                     GSpacing nLineSpace,
410
                     GDALRasterIOExtraArg *psExtraArg) override;
411
};
412
413
CPLErr GDALFootprintCombinedMaskBand::IReadBlock(int nBlockXOff, int nBlockYOff,
414
                                                 void *pData)
415
0
{
416
0
    int nWindowXSize;
417
0
    int nWindowYSize;
418
0
    m_apoSrcBands[0]->GetActualBlockSize(nBlockXOff, nBlockYOff, &nWindowXSize,
419
0
                                         &nWindowYSize);
420
0
    GDALRasterIOExtraArg sExtraArg;
421
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
422
0
    return IRasterIO(GF_Read, nBlockXOff * nBlockXSize,
423
0
                     nBlockYOff * nBlockYSize, nWindowXSize, nWindowYSize,
424
0
                     pData, nWindowXSize, nWindowYSize, GDT_UInt8, 1,
425
0
                     nBlockXSize, &sExtraArg);
426
0
}
427
428
CPLErr GDALFootprintCombinedMaskBand::IRasterIO(
429
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
430
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
431
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
432
0
{
433
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
434
0
        eBufType == GDT_UInt8 && nPixelSpace == 1)
435
0
    {
436
        // Request when band seen as the mask band for GDALPolygonize()
437
0
        {
438
0
            GByte *pabyData = static_cast<GByte *>(pData);
439
0
            for (int iY = 0; iY < nYSize; ++iY)
440
0
            {
441
0
                memset(pabyData, m_bUnion ? 0 : 1, nXSize);
442
0
                pabyData += nLineSpace;
443
0
            }
444
0
        }
445
446
0
        std::vector<GByte> abyTmp(static_cast<size_t>(nXSize) * nYSize);
447
0
        for (auto poBand : m_apoSrcBands)
448
0
        {
449
0
            if (poBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
450
0
                                 abyTmp.data(), nBufXSize, nBufYSize, GDT_UInt8,
451
0
                                 1, nXSize, psExtraArg) != CE_None)
452
0
            {
453
0
                return CE_Failure;
454
0
            }
455
0
            GByte *pabyData = static_cast<GByte *>(pData);
456
0
            size_t iTmp = 0;
457
0
            for (int iY = 0; iY < nYSize; ++iY)
458
0
            {
459
0
                if (m_bUnion)
460
0
                {
461
0
                    for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
462
0
                    {
463
0
                        if (abyTmp[iTmp])
464
0
                            pabyData[iX] = 1;
465
0
                    }
466
0
                }
467
0
                else
468
0
                {
469
0
                    for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
470
0
                    {
471
0
                        if (abyTmp[iTmp] == 0)
472
0
                            pabyData[iX] = 0;
473
0
                    }
474
0
                }
475
0
                pabyData += nLineSpace;
476
0
            }
477
0
        }
478
479
0
        return CE_None;
480
0
    }
481
482
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
483
0
        eBufType == GDT_Int64 &&
484
0
        nPixelSpace == static_cast<int>(sizeof(int64_t)) &&
485
0
        (nLineSpace % nPixelSpace) == 0)
486
0
    {
487
        // Request when band seen as the value band for GDALPolygonize()
488
0
        {
489
0
            int64_t *panData = static_cast<int64_t *>(pData);
490
0
            for (int iY = 0; iY < nYSize; ++iY)
491
0
            {
492
0
                if (m_bUnion)
493
0
                {
494
0
                    memset(panData, 0, nXSize * sizeof(int64_t));
495
0
                }
496
0
                else
497
0
                {
498
0
                    int64_t nOne = 1;
499
0
                    GDALCopyWords(&nOne, GDT_Int64, 0, panData, GDT_Int64,
500
0
                                  sizeof(int64_t), nXSize);
501
0
                }
502
0
                panData += (nLineSpace / nPixelSpace);
503
0
            }
504
0
        }
505
506
0
        std::vector<GByte> abyTmp(static_cast<size_t>(nXSize) * nYSize);
507
0
        for (auto poBand : m_apoSrcBands)
508
0
        {
509
0
            if (poBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
510
0
                                 abyTmp.data(), nBufXSize, nBufYSize, GDT_UInt8,
511
0
                                 1, nXSize, psExtraArg) != CE_None)
512
0
            {
513
0
                return CE_Failure;
514
0
            }
515
0
            size_t iTmp = 0;
516
0
            int64_t *panData = static_cast<int64_t *>(pData);
517
0
            for (int iY = 0; iY < nYSize; ++iY)
518
0
            {
519
0
                if (m_bUnion)
520
0
                {
521
0
                    for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
522
0
                    {
523
0
                        if (abyTmp[iTmp])
524
0
                            panData[iX] = 1;
525
0
                    }
526
0
                }
527
0
                else
528
0
                {
529
0
                    for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
530
0
                    {
531
0
                        if (abyTmp[iTmp] == 0)
532
0
                            panData[iX] = 0;
533
0
                    }
534
0
                }
535
0
                panData += (nLineSpace / nPixelSpace);
536
0
            }
537
0
        }
538
0
        return CE_None;
539
0
    }
540
541
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
542
0
                                     pData, nBufXSize, nBufYSize, eBufType,
543
0
                                     nPixelSpace, nLineSpace, psExtraArg);
544
0
}
545
546
/************************************************************************/
547
/*                    GetOutputLayerAndUpdateDstDS()                    */
548
/************************************************************************/
549
550
static OGRLayer *
551
GetOutputLayerAndUpdateDstDS(const char *pszDest, GDALDatasetH &hDstDS,
552
                             GDALDataset *poSrcDS,
553
                             const GDALFootprintOptions *psOptions)
554
0
{
555
556
0
    if (pszDest == nullptr)
557
0
        pszDest = GDALGetDescription(hDstDS);
558
559
    /* -------------------------------------------------------------------- */
560
    /*      Create output dataset if needed                                 */
561
    /* -------------------------------------------------------------------- */
562
0
    const bool bCreateOutput = psOptions->bCreateOutput || hDstDS == nullptr;
563
564
0
    GDALDriverH hDriver = nullptr;
565
0
    if (bCreateOutput)
566
0
    {
567
0
        std::string osFormat(psOptions->osFormat);
568
0
        if (osFormat.empty())
569
0
        {
570
0
            const auto aoDrivers = GetOutputDriversFor(pszDest, GDAL_OF_VECTOR);
571
0
            if (aoDrivers.empty())
572
0
            {
573
0
                CPLError(CE_Failure, CPLE_AppDefined,
574
0
                         "Cannot guess driver for %s", pszDest);
575
0
                return nullptr;
576
0
            }
577
0
            else
578
0
            {
579
0
                if (aoDrivers.size() > 1)
580
0
                {
581
0
                    CPLError(CE_Warning, CPLE_AppDefined,
582
0
                             "Several drivers matching %s extension. Using %s",
583
0
                             CPLGetExtensionSafe(pszDest).c_str(),
584
0
                             aoDrivers[0].c_str());
585
0
                }
586
0
                osFormat = aoDrivers[0];
587
0
            }
588
0
        }
589
590
        /* ----------------------------------------------------------------- */
591
        /*      Find the output driver. */
592
        /* ----------------------------------------------------------------- */
593
0
        hDriver = GDALGetDriverByName(osFormat.c_str());
594
0
        CSLConstList papszDriverMD =
595
0
            hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
596
0
        if (hDriver == nullptr ||
597
0
            !CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_VECTOR,
598
0
                                              "FALSE")) ||
599
0
            !CPLTestBool(
600
0
                CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
601
0
        {
602
0
            CPLError(CE_Failure, CPLE_NotSupported,
603
0
                     "Output driver `%s' not recognised or does not support "
604
0
                     "direct output file creation.",
605
0
                     osFormat.c_str());
606
0
            return nullptr;
607
0
        }
608
609
0
        hDstDS = GDALCreate(hDriver, pszDest, 0, 0, 0, GDT_Unknown,
610
0
                            psOptions->aosDSCO.List());
611
0
        if (!hDstDS)
612
0
        {
613
0
            return nullptr;
614
0
        }
615
0
    }
616
617
    /* -------------------------------------------------------------------- */
618
    /*      Open or create target layer.                                    */
619
    /* -------------------------------------------------------------------- */
620
0
    auto poDstDS = GDALDataset::FromHandle(hDstDS);
621
0
    OGRLayer *poLayer = nullptr;
622
623
0
    if (!bCreateOutput)
624
0
    {
625
0
        if (poDstDS->GetLayerCount() == 1 && poDstDS->GetDriver() &&
626
0
            EQUAL(poDstDS->GetDriver()->GetDescription(), "ESRI Shapefile"))
627
0
        {
628
0
            poLayer = poDstDS->GetLayer(0);
629
0
        }
630
0
        else if (!psOptions->osDestLayerName.empty())
631
0
        {
632
0
            poLayer =
633
0
                poDstDS->GetLayerByName(psOptions->osDestLayerName.c_str());
634
0
            if (!poLayer)
635
0
            {
636
0
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot find layer %s",
637
0
                         psOptions->osDestLayerName.c_str());
638
0
                return nullptr;
639
0
            }
640
0
        }
641
0
        else
642
0
        {
643
0
            poLayer = poDstDS->GetLayerByName(DEFAULT_LAYER_NAME);
644
0
        }
645
0
    }
646
647
0
    if (!poLayer)
648
0
    {
649
0
        std::string osDestLayerName = psOptions->osDestLayerName;
650
0
        if (osDestLayerName.empty())
651
0
        {
652
0
            if (poDstDS->GetDriver() &&
653
0
                EQUAL(poDstDS->GetDriver()->GetDescription(), "ESRI Shapefile"))
654
0
            {
655
0
                osDestLayerName = CPLGetBasenameSafe(pszDest);
656
0
            }
657
0
            else
658
0
            {
659
0
                osDestLayerName = DEFAULT_LAYER_NAME;
660
0
            }
661
0
        }
662
663
0
        OGRSpatialReferenceRefCountedPtr poSRS;
664
0
        if (psOptions->bOutCSGeoref)
665
0
        {
666
0
            if (!psOptions->oOutputSRS.IsEmpty())
667
0
            {
668
0
                poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(
669
0
                    psOptions->oOutputSRS);
670
0
            }
671
0
            else if (auto poSrcSRS = poSrcDS->GetSpatialRef())
672
0
            {
673
0
                poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(poSrcSRS);
674
0
            }
675
0
        }
676
677
0
        poLayer = poDstDS->CreateLayer(
678
0
            osDestLayerName.c_str(), poSRS.get(),
679
0
            psOptions->bSplitPolys ? wkbPolygon : wkbMultiPolygon,
680
0
            const_cast<char **>(psOptions->aosLCO.List()));
681
682
0
        if (!psOptions->osLocationFieldName.empty())
683
0
        {
684
0
            OGRFieldDefn oFieldDefn(psOptions->osLocationFieldName.c_str(),
685
0
                                    OFTString);
686
0
            if (poLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
687
0
                return nullptr;
688
0
        }
689
0
    }
690
691
0
    return poLayer;
692
0
}
693
694
/************************************************************************/
695
/*                 GeoTransformCoordinateTransformation                 */
696
/************************************************************************/
697
698
class GeoTransformCoordinateTransformation final
699
    : public OGRCoordinateTransformation
700
{
701
    const GDALGeoTransform &m_gt;
702
703
  public:
704
    explicit GeoTransformCoordinateTransformation(const GDALGeoTransform &gt)
705
0
        : m_gt(gt)
706
0
    {
707
0
    }
708
709
    const OGRSpatialReference *GetSourceCS() const override;
710
711
    const OGRSpatialReference *GetTargetCS() const override
712
0
    {
713
0
        return nullptr;
714
0
    }
715
716
    OGRCoordinateTransformation *Clone() const override
717
0
    {
718
0
        return new GeoTransformCoordinateTransformation(m_gt);
719
0
    }
720
721
    OGRCoordinateTransformation *GetInverse() const override
722
0
    {
723
0
        CPLError(CE_Failure, CPLE_NotSupported,
724
0
                 "GeoTransformCoordinateTransformation::GetInverse() not "
725
0
                 "implemented");
726
0
        return nullptr;
727
0
    }
728
729
    int Transform(size_t nCount, double *x, double *y, double * /* z */,
730
                  double * /* t */, int *pabSuccess) override
731
0
    {
732
0
        for (size_t i = 0; i < nCount; ++i)
733
0
        {
734
0
            const double X = m_gt.xorig + x[i] * m_gt.xscale + y[i] * m_gt.xrot;
735
0
            const double Y = m_gt.yorig + x[i] * m_gt.yrot + y[i] * m_gt.yscale;
736
0
            x[i] = X;
737
0
            y[i] = Y;
738
0
            if (pabSuccess)
739
0
                pabSuccess[i] = TRUE;
740
0
        }
741
0
        return TRUE;
742
0
    }
743
};
744
745
const OGRSpatialReference *
746
GeoTransformCoordinateTransformation::GetSourceCS() const
747
0
{
748
0
    return nullptr;
749
0
}
750
751
/************************************************************************/
752
/*                            CountPoints()                             */
753
/************************************************************************/
754
755
static size_t CountPoints(const OGRGeometry *poGeom)
756
0
{
757
0
    if (poGeom->getGeometryType() == wkbMultiPolygon)
758
0
    {
759
0
        size_t n = 0;
760
0
        for (auto *poPoly : poGeom->toMultiPolygon())
761
0
        {
762
0
            n += CountPoints(poPoly);
763
0
        }
764
0
        return n;
765
0
    }
766
0
    else if (poGeom->getGeometryType() == wkbPolygon)
767
0
    {
768
0
        size_t n = 0;
769
0
        for (auto *poRing : poGeom->toPolygon())
770
0
        {
771
0
            n += poRing->getNumPoints() - 1;
772
0
        }
773
0
        return n;
774
0
    }
775
0
    return 0;
776
0
}
777
778
/************************************************************************/
779
/*                   GetMinDistanceBetweenTwoPoints()                   */
780
/************************************************************************/
781
782
static double GetMinDistanceBetweenTwoPoints(const OGRGeometry *poGeom)
783
0
{
784
0
    if (poGeom->getGeometryType() == wkbMultiPolygon)
785
0
    {
786
0
        double v = std::numeric_limits<double>::max();
787
0
        for (auto *poPoly : poGeom->toMultiPolygon())
788
0
        {
789
0
            v = std::min(v, GetMinDistanceBetweenTwoPoints(poPoly));
790
0
        }
791
0
        return v;
792
0
    }
793
0
    else if (poGeom->getGeometryType() == wkbPolygon)
794
0
    {
795
0
        double v = std::numeric_limits<double>::max();
796
0
        for (auto *poRing : poGeom->toPolygon())
797
0
        {
798
0
            v = std::min(v, GetMinDistanceBetweenTwoPoints(poRing));
799
0
        }
800
0
        return v;
801
0
    }
802
0
    else if (poGeom->getGeometryType() == wkbLineString)
803
0
    {
804
0
        double v = std::numeric_limits<double>::max();
805
0
        const auto poLS = poGeom->toLineString();
806
0
        const int nNumPoints = poLS->getNumPoints();
807
0
        for (int i = 0; i < nNumPoints - 1; ++i)
808
0
        {
809
0
            const double x1 = poLS->getX(i);
810
0
            const double y1 = poLS->getY(i);
811
0
            const double x2 = poLS->getX(i + 1);
812
0
            const double y2 = poLS->getY(i + 1);
813
0
            const double d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
814
0
            if (d > 0)
815
0
                v = std::min(v, d);
816
0
        }
817
0
        return sqrt(v);
818
0
    }
819
0
    return 0;
820
0
}
821
822
/************************************************************************/
823
/*                        GDALFootprintProcess()                        */
824
/************************************************************************/
825
826
static bool GDALFootprintProcess(GDALDataset *poSrcDS, OGRLayer *poDstLayer,
827
                                 const GDALFootprintOptions *psOptions)
828
0
{
829
0
    std::unique_ptr<OGRCoordinateTransformation> poCT_SRS;
830
0
    const OGRSpatialReference *poDstSRS = poDstLayer->GetSpatialRef();
831
0
    if (!psOptions->oOutputSRS.IsEmpty())
832
0
        poDstSRS = &(psOptions->oOutputSRS);
833
0
    if (poDstSRS)
834
0
    {
835
0
        auto poSrcSRS = poSrcDS->GetSpatialRef();
836
0
        if (!poSrcSRS)
837
0
        {
838
0
            CPLError(CE_Failure, CPLE_AppDefined,
839
0
                     "Output layer has CRS, but input is not georeferenced");
840
0
            return false;
841
0
        }
842
0
        poCT_SRS.reset(OGRCreateCoordinateTransformation(poSrcSRS, poDstSRS));
843
0
        if (!poCT_SRS)
844
0
            return false;
845
0
    }
846
847
0
    std::vector<int> anBands = psOptions->anBands;
848
0
    const int nBandCount = poSrcDS->GetRasterCount();
849
0
    if (anBands.empty())
850
0
    {
851
0
        for (int i = 1; i <= nBandCount; ++i)
852
0
            anBands.push_back(i);
853
0
    }
854
855
0
    std::vector<GDALRasterBand *> apoSrcMaskBands;
856
0
    const CPLStringList aosSrcNoData(
857
0
        CSLTokenizeString2(psOptions->osSrcNoData.c_str(), " ", 0));
858
0
    std::vector<double> adfSrcNoData;
859
0
    if (!psOptions->osSrcNoData.empty())
860
0
    {
861
0
        if (aosSrcNoData.size() != 1 &&
862
0
            static_cast<size_t>(aosSrcNoData.size()) != anBands.size())
863
0
        {
864
0
            CPLError(CE_Failure, CPLE_AppDefined,
865
0
                     "Number of values in -srcnodata should be 1 or the number "
866
0
                     "of bands");
867
0
            return false;
868
0
        }
869
0
        for (int i = 0; i < aosSrcNoData.size(); ++i)
870
0
        {
871
0
            adfSrcNoData.emplace_back(CPLAtof(aosSrcNoData[i]));
872
0
        }
873
0
    }
874
0
    bool bGlobalMask = true;
875
0
    std::vector<std::unique_ptr<GDALRasterBand>> apoTmpNoDataMaskBands;
876
0
    for (size_t i = 0; i < anBands.size(); ++i)
877
0
    {
878
0
        const int nBand = anBands[i];
879
0
        if (nBand <= 0 || nBand > nBandCount)
880
0
        {
881
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid band number: %d",
882
0
                     nBand);
883
0
            return false;
884
0
        }
885
0
        auto poBand = poSrcDS->GetRasterBand(nBand);
886
0
        if (!adfSrcNoData.empty())
887
0
        {
888
0
            bGlobalMask = false;
889
0
            apoTmpNoDataMaskBands.emplace_back(
890
0
                std::make_unique<GDALNoDataMaskBand>(
891
0
                    poBand, adfSrcNoData.size() == 1 ? adfSrcNoData[0]
892
0
                                                     : adfSrcNoData[i]));
893
0
            apoSrcMaskBands.push_back(apoTmpNoDataMaskBands.back().get());
894
0
        }
895
0
        else
896
0
        {
897
0
            GDALRasterBand *poMaskBand;
898
0
            const int nMaskFlags = poBand->GetMaskFlags();
899
0
            if (poBand->GetColorInterpretation() == GCI_AlphaBand)
900
0
            {
901
0
                poMaskBand = poBand;
902
0
            }
903
0
            else
904
0
            {
905
0
                if ((nMaskFlags & GMF_PER_DATASET) == 0)
906
0
                {
907
0
                    bGlobalMask = false;
908
0
                }
909
0
                poMaskBand = poBand->GetMaskBand();
910
0
            }
911
0
            if (psOptions->nOvrIndex >= 0)
912
0
            {
913
0
                if (nMaskFlags == GMF_NODATA)
914
0
                {
915
                    // If the mask band is based on nodata, we don't need
916
                    // to check the overviews of the mask band, but we
917
                    // can take the mask band of the overviews
918
0
                    auto poOvrBand = poBand->GetOverview(psOptions->nOvrIndex);
919
0
                    if (!poOvrBand)
920
0
                    {
921
0
                        if (poBand->GetOverviewCount() == 0)
922
0
                        {
923
0
                            CPLError(
924
0
                                CE_Failure, CPLE_AppDefined,
925
0
                                "Overview index %d invalid for this dataset. "
926
0
                                "Bands of this dataset have no "
927
0
                                "precomputed overviews",
928
0
                                psOptions->nOvrIndex);
929
0
                        }
930
0
                        else
931
0
                        {
932
0
                            CPLError(
933
0
                                CE_Failure, CPLE_AppDefined,
934
0
                                "Overview index %d invalid for this dataset. "
935
0
                                "Value should be in [0,%d] range",
936
0
                                psOptions->nOvrIndex,
937
0
                                poBand->GetOverviewCount() - 1);
938
0
                        }
939
0
                        return false;
940
0
                    }
941
0
                    if (poOvrBand->GetMaskFlags() != GMF_NODATA)
942
0
                    {
943
0
                        CPLError(CE_Failure, CPLE_AppDefined,
944
0
                                 "poOvrBand->GetMaskFlags() != GMF_NODATA");
945
0
                        return false;
946
0
                    }
947
0
                    poMaskBand = poOvrBand->GetMaskBand();
948
0
                }
949
0
                else
950
0
                {
951
0
                    poMaskBand = poMaskBand->GetOverview(psOptions->nOvrIndex);
952
0
                    if (!poMaskBand)
953
0
                    {
954
0
                        if (poBand->GetMaskBand()->GetOverviewCount() == 0)
955
0
                        {
956
0
                            CPLError(
957
0
                                CE_Failure, CPLE_AppDefined,
958
0
                                "Overview index %d invalid for this dataset. "
959
0
                                "Mask bands of this dataset have no "
960
0
                                "precomputed overviews",
961
0
                                psOptions->nOvrIndex);
962
0
                        }
963
0
                        else
964
0
                        {
965
0
                            CPLError(
966
0
                                CE_Failure, CPLE_AppDefined,
967
0
                                "Overview index %d invalid for this dataset. "
968
0
                                "Value should be in [0,%d] range",
969
0
                                psOptions->nOvrIndex,
970
0
                                poBand->GetMaskBand()->GetOverviewCount() - 1);
971
0
                        }
972
0
                        return false;
973
0
                    }
974
0
                }
975
0
            }
976
0
            apoSrcMaskBands.push_back(poMaskBand);
977
0
        }
978
0
    }
979
980
0
    std::unique_ptr<OGRCoordinateTransformation> poCT_GT;
981
0
    GDALGeoTransform gt;
982
0
    if (psOptions->bOutCSGeoref && poSrcDS->GetGeoTransform(gt) == CE_None)
983
0
    {
984
0
        auto poMaskBand = apoSrcMaskBands[0];
985
0
        gt.Rescale(double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize(),
986
0
                   double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize());
987
0
        poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(gt);
988
0
    }
989
0
    else if (psOptions->bOutCSGeorefRequested)
990
0
    {
991
0
        CPLError(CE_Failure, CPLE_AppDefined,
992
0
                 "Georeferenced coordinates requested, but "
993
0
                 "input dataset has no geotransform.");
994
0
        return false;
995
0
    }
996
0
    else if (psOptions->nOvrIndex >= 0)
997
0
    {
998
        // Transform from overview pixel coordinates to full resolution
999
        // pixel coordinates
1000
0
        auto poMaskBand = apoSrcMaskBands[0];
1001
0
        gt.xscale = double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize();
1002
0
        gt.xrot = 0;
1003
0
        gt.yrot = 0;
1004
0
        gt.yscale = double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize();
1005
0
        poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(gt);
1006
0
    }
1007
1008
0
    std::unique_ptr<GDALRasterBand> poMaskForRasterize;
1009
0
    if (bGlobalMask || anBands.size() == 1)
1010
0
    {
1011
0
        poMaskForRasterize =
1012
0
            std::make_unique<GDALFootprintMaskBand>(apoSrcMaskBands[0]);
1013
0
    }
1014
0
    else
1015
0
    {
1016
0
        poMaskForRasterize = std::make_unique<GDALFootprintCombinedMaskBand>(
1017
0
            apoSrcMaskBands, psOptions->bCombineBandsUnion);
1018
0
    }
1019
1020
0
    auto hBand = GDALRasterBand::ToHandle(poMaskForRasterize.get());
1021
0
    auto poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
1022
0
    const CPLErr eErr =
1023
0
        GDALPolygonize(hBand, hBand, OGRLayer::ToHandle(poMemLayer.get()),
1024
0
                       /* iPixValField = */ -1,
1025
0
                       /* papszOptions = */ nullptr, psOptions->pfnProgress,
1026
0
                       psOptions->pProgressData);
1027
0
    if (eErr != CE_None)
1028
0
    {
1029
0
        return false;
1030
0
    }
1031
1032
0
    if (!psOptions->bSplitPolys)
1033
0
    {
1034
0
        auto poMP = std::make_unique<OGRMultiPolygon>();
1035
0
        for (auto &&poFeature : poMemLayer.get())
1036
0
        {
1037
0
            auto poGeom =
1038
0
                std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
1039
0
            CPLAssert(poGeom);
1040
0
            if (poGeom->getGeometryType() == wkbPolygon)
1041
0
            {
1042
0
                poMP->addGeometry(std::move(poGeom));
1043
0
            }
1044
0
        }
1045
0
        poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
1046
0
        auto poFeature =
1047
0
            std::make_unique<OGRFeature>(poMemLayer->GetLayerDefn());
1048
0
        poFeature->SetGeometryDirectly(poMP.release());
1049
0
        CPL_IGNORE_RET_VAL(poMemLayer->CreateFeature(std::move(poFeature)));
1050
0
    }
1051
1052
0
    for (auto &&poFeature : poMemLayer.get())
1053
0
    {
1054
0
        auto poGeom = std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
1055
0
        CPLAssert(poGeom);
1056
0
        if (poGeom->IsEmpty())
1057
0
            continue;
1058
1059
0
        auto poDstFeature =
1060
0
            std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
1061
0
        poDstFeature->SetFrom(poFeature.get());
1062
1063
0
        if (poCT_GT)
1064
0
        {
1065
0
            if (poGeom->transform(poCT_GT.get()) != OGRERR_NONE)
1066
0
                return false;
1067
0
        }
1068
1069
0
        if (psOptions->dfDensifyDistance > 0)
1070
0
        {
1071
0
            OGREnvelope sEnvelope;
1072
0
            poGeom->getEnvelope(&sEnvelope);
1073
            // Some sanity check to avoid insane memory allocations
1074
0
            if (sEnvelope.MaxX - sEnvelope.MinX >
1075
0
                    1e6 * psOptions->dfDensifyDistance ||
1076
0
                sEnvelope.MaxY - sEnvelope.MinY >
1077
0
                    1e6 * psOptions->dfDensifyDistance)
1078
0
            {
1079
0
                CPLError(CE_Failure, CPLE_AppDefined,
1080
0
                         "Densification distance too small compared to "
1081
0
                         "geometry extent");
1082
0
                return false;
1083
0
            }
1084
0
            poGeom->segmentize(psOptions->dfDensifyDistance);
1085
0
        }
1086
1087
0
        if (poCT_SRS)
1088
0
        {
1089
0
            if (poGeom->transform(poCT_SRS.get()) != OGRERR_NONE)
1090
0
                return false;
1091
0
        }
1092
1093
0
        if (psOptions->dfMinRingArea != 0)
1094
0
        {
1095
0
            if (poGeom->getGeometryType() == wkbMultiPolygon)
1096
0
            {
1097
0
                auto poMP = std::make_unique<OGRMultiPolygon>();
1098
0
                for (auto *poPoly : poGeom->toMultiPolygon())
1099
0
                {
1100
0
                    auto poNewPoly = std::make_unique<OGRPolygon>();
1101
0
                    for (auto *poRing : poPoly)
1102
0
                    {
1103
0
                        if (poRing->get_Area() >= psOptions->dfMinRingArea)
1104
0
                        {
1105
0
                            poNewPoly->addRing(poRing);
1106
0
                        }
1107
0
                    }
1108
0
                    if (!poNewPoly->IsEmpty())
1109
0
                        poMP->addGeometry(std::move(poNewPoly));
1110
0
                }
1111
0
                poGeom = std::move(poMP);
1112
0
            }
1113
0
            else if (poGeom->getGeometryType() == wkbPolygon)
1114
0
            {
1115
0
                auto poNewPoly = std::make_unique<OGRPolygon>();
1116
0
                for (auto *poRing : poGeom->toPolygon())
1117
0
                {
1118
0
                    if (poRing->get_Area() >= psOptions->dfMinRingArea)
1119
0
                    {
1120
0
                        poNewPoly->addRing(poRing);
1121
0
                    }
1122
0
                }
1123
0
                poGeom = std::move(poNewPoly);
1124
0
            }
1125
0
            if (poGeom->IsEmpty())
1126
0
                continue;
1127
0
        }
1128
1129
0
        const auto GeomIsNullOrEmpty = [&poGeom]
1130
0
        { return !poGeom || poGeom->IsEmpty(); };
1131
1132
0
        if (psOptions->bConvexHull)
1133
0
        {
1134
0
            poGeom.reset(poGeom->ConvexHull());
1135
0
            if (GeomIsNullOrEmpty())
1136
0
                continue;
1137
0
        }
1138
1139
0
        const auto DoSimplification = [&poMemLayer, &poGeom](double dfTolerance)
1140
0
        {
1141
0
            std::string osLastErrorMsg;
1142
0
            {
1143
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1144
0
                CPLErrorReset();
1145
0
                poGeom.reset(poGeom->Simplify(dfTolerance));
1146
0
                osLastErrorMsg = CPLGetLastErrorMsg();
1147
0
            }
1148
0
            if (!poGeom || poGeom->IsEmpty())
1149
0
            {
1150
0
                if (poMemLayer->GetFeatureCount(false) == 1)
1151
0
                {
1152
0
                    if (!osLastErrorMsg.empty())
1153
0
                        CPLError(CE_Failure, CPLE_AppDefined, "%s",
1154
0
                                 osLastErrorMsg.c_str());
1155
0
                    else
1156
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1157
0
                                 "Simplification resulted in empty geometry");
1158
0
                    return false;
1159
0
                }
1160
0
                if (!osLastErrorMsg.empty())
1161
0
                    CPLError(CE_Warning, CPLE_AppDefined, "%s",
1162
0
                             osLastErrorMsg.c_str());
1163
0
            }
1164
0
            return true;
1165
0
        };
1166
1167
0
        if (psOptions->dfSimplifyTolerance != 0)
1168
0
        {
1169
0
            if (!DoSimplification(psOptions->dfSimplifyTolerance))
1170
0
                return false;
1171
0
            if (GeomIsNullOrEmpty())
1172
0
                continue;
1173
0
        }
1174
1175
0
        if (psOptions->nMaxPoints > 0 &&
1176
0
            CountPoints(poGeom.get()) >
1177
0
                static_cast<size_t>(psOptions->nMaxPoints))
1178
0
        {
1179
0
            OGREnvelope sEnvelope;
1180
0
            poGeom->getEnvelope(&sEnvelope);
1181
0
            double tolMin = GetMinDistanceBetweenTwoPoints(poGeom.get());
1182
0
            double tolMax = std::max(sEnvelope.MaxY - sEnvelope.MinY,
1183
0
                                     sEnvelope.MaxX - sEnvelope.MinX);
1184
0
            for (int i = 0; i < 20; ++i)
1185
0
            {
1186
0
                const double tol = (tolMin + tolMax) / 2;
1187
0
                std::unique_ptr<OGRGeometry> poSimplifiedGeom(
1188
0
                    poGeom->Simplify(tol));
1189
0
                if (!poSimplifiedGeom || poSimplifiedGeom->IsEmpty())
1190
0
                {
1191
0
                    tolMax = tol;
1192
0
                    continue;
1193
0
                }
1194
0
                const auto nPoints = CountPoints(poSimplifiedGeom.get());
1195
0
                if (nPoints == static_cast<size_t>(psOptions->nMaxPoints))
1196
0
                {
1197
0
                    tolMax = tol;
1198
0
                    break;
1199
0
                }
1200
0
                else if (nPoints < static_cast<size_t>(psOptions->nMaxPoints))
1201
0
                {
1202
0
                    tolMax = tol;
1203
0
                }
1204
0
                else
1205
0
                {
1206
0
                    tolMin = tol;
1207
0
                }
1208
0
            }
1209
1210
0
            if (!DoSimplification(tolMax))
1211
0
                return false;
1212
0
            if (GeomIsNullOrEmpty())
1213
0
                continue;
1214
0
        }
1215
1216
0
        if (!psOptions->bSplitPolys && poGeom->getGeometryType() == wkbPolygon)
1217
0
            poGeom.reset(
1218
0
                OGRGeometryFactory::forceToMultiPolygon(poGeom.release()));
1219
1220
0
        poDstFeature->SetGeometryDirectly(poGeom.release());
1221
1222
0
        if (!psOptions->osLocationFieldName.empty())
1223
0
        {
1224
1225
0
            std::string osFilename = poSrcDS->GetDescription();
1226
            // Make sure it is a file before building absolute path name.
1227
0
            VSIStatBufL sStatBuf;
1228
0
            if (psOptions->bAbsolutePath &&
1229
0
                CPLIsFilenameRelative(osFilename.c_str()) &&
1230
0
                VSIStatL(osFilename.c_str(), &sStatBuf) == 0)
1231
0
            {
1232
0
                char *pszCurDir = CPLGetCurrentDir();
1233
0
                if (pszCurDir)
1234
0
                {
1235
0
                    osFilename = CPLProjectRelativeFilenameSafe(
1236
0
                        pszCurDir, osFilename.c_str());
1237
0
                    CPLFree(pszCurDir);
1238
0
                }
1239
0
            }
1240
0
            poDstFeature->SetField(psOptions->osLocationFieldName.c_str(),
1241
0
                                   osFilename.c_str());
1242
0
        }
1243
1244
0
        if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
1245
0
        {
1246
0
            return false;
1247
0
        }
1248
0
    }
1249
1250
0
    return true;
1251
0
}
1252
1253
/************************************************************************/
1254
/*                   GDALFootprintAppGetParserUsage()                   */
1255
/************************************************************************/
1256
1257
std::string GDALFootprintAppGetParserUsage()
1258
0
{
1259
0
    try
1260
0
    {
1261
0
        GDALFootprintOptions sOptions;
1262
0
        GDALFootprintOptionsForBinary sOptionsForBinary;
1263
0
        auto argParser =
1264
0
            GDALFootprintAppOptionsGetParser(&sOptions, &sOptionsForBinary);
1265
0
        return argParser->usage();
1266
0
    }
1267
0
    catch (const std::exception &err)
1268
0
    {
1269
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1270
0
                 err.what());
1271
0
        return std::string();
1272
0
    }
1273
0
}
1274
1275
/************************************************************************/
1276
/*                           GDALFootprint()                            */
1277
/************************************************************************/
1278
1279
/* clang-format off */
1280
/**
1281
 * Computes the footprint of a raster.
1282
 *
1283
 * This is the equivalent of the
1284
 * <a href="/programs/gdal_footprint.html">gdal_footprint</a> utility.
1285
 *
1286
 * GDALFootprintOptions* must be allocated and freed with
1287
 * GDALFootprintOptionsNew() and GDALFootprintOptionsFree() respectively.
1288
 * pszDest and hDstDS cannot be used at the same time.
1289
 *
1290
 * @param pszDest the vector destination dataset path or NULL.
1291
 * @param hDstDS the vector destination dataset or NULL.
1292
 * @param hSrcDataset the raster source dataset handle.
1293
 * @param psOptionsIn the options struct returned by GDALFootprintOptionsNew()
1294
 * or NULL.
1295
 * @param pbUsageError pointer to a integer output variable to store if any
1296
 * usage error has occurred or NULL.
1297
 * @return the output dataset (new dataset that must be closed using
1298
 * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
1299
 *
1300
 * @since GDAL 3.8
1301
 */
1302
/* clang-format on */
1303
1304
GDALDatasetH GDALFootprint(const char *pszDest, GDALDatasetH hDstDS,
1305
                           GDALDatasetH hSrcDataset,
1306
                           const GDALFootprintOptions *psOptionsIn,
1307
                           int *pbUsageError)
1308
0
{
1309
0
    if (pszDest == nullptr && hDstDS == nullptr)
1310
0
    {
1311
0
        CPLError(CE_Failure, CPLE_AppDefined,
1312
0
                 "pszDest == NULL && hDstDS == NULL");
1313
1314
0
        if (pbUsageError)
1315
0
            *pbUsageError = TRUE;
1316
0
        return nullptr;
1317
0
    }
1318
0
    if (hSrcDataset == nullptr)
1319
0
    {
1320
0
        CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
1321
1322
0
        if (pbUsageError)
1323
0
            *pbUsageError = TRUE;
1324
0
        return nullptr;
1325
0
    }
1326
0
    if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
1327
0
    {
1328
0
        CPLError(CE_Failure, CPLE_AppDefined,
1329
0
                 "hDstDS != NULL but options that imply creating a new dataset "
1330
0
                 "have been set.");
1331
1332
0
        if (pbUsageError)
1333
0
            *pbUsageError = TRUE;
1334
0
        return nullptr;
1335
0
    }
1336
1337
0
    GDALFootprintOptions *psOptionsToFree = nullptr;
1338
0
    const GDALFootprintOptions *psOptions = psOptionsIn;
1339
0
    if (psOptions == nullptr)
1340
0
    {
1341
0
        psOptionsToFree = GDALFootprintOptionsNew(nullptr, nullptr);
1342
0
        psOptions = psOptionsToFree;
1343
0
    }
1344
1345
0
    const bool bCloseOutDSOnError = hDstDS == nullptr;
1346
1347
0
    auto poSrcDS = GDALDataset::FromHandle(hSrcDataset);
1348
0
    if (poSrcDS->GetRasterCount() == 0)
1349
0
    {
1350
0
        CPLError(CE_Failure, CPLE_AppDefined,
1351
0
                 "Input dataset has no raster band.%s",
1352
0
                 poSrcDS->GetMetadata("SUBDATASETS")
1353
0
                     ? " You need to specify one subdataset."
1354
0
                     : "");
1355
0
        GDALFootprintOptionsFree(psOptionsToFree);
1356
0
        if (bCloseOutDSOnError)
1357
0
            GDALClose(hDstDS);
1358
0
        return nullptr;
1359
0
    }
1360
1361
0
    auto poLayer =
1362
0
        GetOutputLayerAndUpdateDstDS(pszDest, hDstDS, poSrcDS, psOptions);
1363
0
    if (!poLayer)
1364
0
    {
1365
0
        GDALFootprintOptionsFree(psOptionsToFree);
1366
0
        if (hDstDS && bCloseOutDSOnError)
1367
0
            GDALClose(hDstDS);
1368
0
        return nullptr;
1369
0
    }
1370
1371
0
    if (!GDALFootprintProcess(poSrcDS, poLayer, psOptions))
1372
0
    {
1373
0
        GDALFootprintOptionsFree(psOptionsToFree);
1374
0
        if (bCloseOutDSOnError)
1375
0
            GDALClose(hDstDS);
1376
0
        return nullptr;
1377
0
    }
1378
1379
0
    GDALFootprintOptionsFree(psOptionsToFree);
1380
1381
0
    return hDstDS;
1382
0
}
1383
1384
/************************************************************************/
1385
/*                      GDALFootprintOptionsNew()                       */
1386
/************************************************************************/
1387
1388
/**
1389
 * Allocates a GDALFootprintOptions struct.
1390
 *
1391
 * @param papszArgv NULL terminated list of options (potentially including
1392
 * filename and open options too), or NULL. The accepted options are the ones of
1393
 * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
1394
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1395
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1396
 * GDALFootprintOptionsForBinaryNew() prior to this function. Will be filled
1397
 * with potentially present filename, open options,...
1398
 * @return pointer to the allocated GDALFootprintOptions struct. Must be freed
1399
 * with GDALFootprintOptionsFree().
1400
 *
1401
 * @since GDAL 3.8
1402
 */
1403
1404
GDALFootprintOptions *
1405
GDALFootprintOptionsNew(char **papszArgv,
1406
                        GDALFootprintOptionsForBinary *psOptionsForBinary)
1407
0
{
1408
0
    auto psOptions = std::make_unique<GDALFootprintOptions>();
1409
1410
    /* -------------------------------------------------------------------- */
1411
    /*      Parse arguments.                                                */
1412
    /* -------------------------------------------------------------------- */
1413
1414
0
    CPLStringList aosArgv;
1415
1416
0
    if (papszArgv)
1417
0
    {
1418
0
        const int nArgc = CSLCount(papszArgv);
1419
0
        for (int i = 0; i < nArgc; i++)
1420
0
        {
1421
0
            aosArgv.AddString(papszArgv[i]);
1422
0
        }
1423
0
    }
1424
1425
0
    try
1426
0
    {
1427
0
        auto argParser = GDALFootprintAppOptionsGetParser(psOptions.get(),
1428
0
                                                          psOptionsForBinary);
1429
1430
0
        argParser->parse_args_without_binary_name(aosArgv.List());
1431
1432
0
        if (argParser->is_used("-t_srs"))
1433
0
        {
1434
1435
0
            const std::string osVal(argParser->get<std::string>("-t_srs"));
1436
0
            if (psOptions->oOutputSRS.SetFromUserInput(osVal.c_str()) !=
1437
0
                OGRERR_NONE)
1438
0
            {
1439
0
                CPLError(CE_Failure, CPLE_AppDefined,
1440
0
                         "Failed to process SRS definition: %s", osVal.c_str());
1441
0
                return nullptr;
1442
0
            }
1443
0
            psOptions->oOutputSRS.SetAxisMappingStrategy(
1444
0
                OAMS_TRADITIONAL_GIS_ORDER);
1445
0
        }
1446
1447
0
        if (argParser->is_used("-max_points"))
1448
0
        {
1449
0
            const std::string maxPoints{
1450
0
                argParser->get<std::string>("-max_points")};
1451
0
            if (maxPoints == "unlimited")
1452
0
            {
1453
0
                psOptions->nMaxPoints = 0;
1454
0
            }
1455
0
            else
1456
0
            {
1457
0
                psOptions->nMaxPoints = atoi(maxPoints.c_str());
1458
0
                if (psOptions->nMaxPoints > 0 && psOptions->nMaxPoints < 3)
1459
0
                {
1460
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1461
0
                             "Invalid value for -max_points");
1462
0
                    return nullptr;
1463
0
                }
1464
0
            }
1465
0
        }
1466
1467
0
        psOptions->bCreateOutput = !psOptions->osFormat.empty();
1468
0
    }
1469
0
    catch (const std::exception &err)
1470
0
    {
1471
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1472
0
                 err.what());
1473
0
        return nullptr;
1474
0
    }
1475
1476
0
    if (!psOptions->bOutCSGeoref && !psOptions->oOutputSRS.IsEmpty())
1477
0
    {
1478
0
        CPLError(CE_Failure, CPLE_AppDefined,
1479
0
                 "-t_cs pixel and -t_srs are mutually exclusive.");
1480
0
        return nullptr;
1481
0
    }
1482
1483
0
    if (psOptions->bClearLocation)
1484
0
    {
1485
0
        psOptions->osLocationFieldName.clear();
1486
0
    }
1487
1488
0
    if (psOptionsForBinary)
1489
0
    {
1490
0
        psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
1491
0
        psOptionsForBinary->osFormat = psOptions->osFormat;
1492
0
        psOptionsForBinary->osDestLayerName = psOptions->osDestLayerName;
1493
0
    }
1494
1495
0
    return psOptions.release();
1496
0
}
1497
1498
/************************************************************************/
1499
/*                      GDALFootprintOptionsFree()                      */
1500
/************************************************************************/
1501
1502
/**
1503
 * Frees the GDALFootprintOptions struct.
1504
 *
1505
 * @param psOptions the options struct for GDALFootprint().
1506
 *
1507
 * @since GDAL 3.8
1508
 */
1509
1510
void GDALFootprintOptionsFree(GDALFootprintOptions *psOptions)
1511
0
{
1512
0
    delete psOptions;
1513
0
}
1514
1515
/************************************************************************/
1516
/*                  GDALFootprintOptionsSetProgress()                   */
1517
/************************************************************************/
1518
1519
/**
1520
 * Set a progress function.
1521
 *
1522
 * @param psOptions the options struct for GDALFootprint().
1523
 * @param pfnProgress the progress callback.
1524
 * @param pProgressData the user data for the progress callback.
1525
 *
1526
 * @since GDAL 3.8
1527
 */
1528
1529
void GDALFootprintOptionsSetProgress(GDALFootprintOptions *psOptions,
1530
                                     GDALProgressFunc pfnProgress,
1531
                                     void *pProgressData)
1532
0
{
1533
0
    psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1534
0
    psOptions->pProgressData = pProgressData;
1535
0
}