Coverage Report

Created: 2026-02-14 06:52

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
        std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
664
0
        if (psOptions->bOutCSGeoref)
665
0
        {
666
0
            if (!psOptions->oOutputSRS.IsEmpty())
667
0
            {
668
0
                poSRS.reset(psOptions->oOutputSRS.Clone());
669
0
            }
670
0
            else if (auto poSrcSRS = poSrcDS->GetSpatialRef())
671
0
            {
672
0
                poSRS.reset(poSrcSRS->Clone());
673
0
            }
674
0
        }
675
676
0
        poLayer = poDstDS->CreateLayer(
677
0
            osDestLayerName.c_str(), poSRS.get(),
678
0
            psOptions->bSplitPolys ? wkbPolygon : wkbMultiPolygon,
679
0
            const_cast<char **>(psOptions->aosLCO.List()));
680
681
0
        if (!psOptions->osLocationFieldName.empty())
682
0
        {
683
0
            OGRFieldDefn oFieldDefn(psOptions->osLocationFieldName.c_str(),
684
0
                                    OFTString);
685
0
            if (poLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
686
0
                return nullptr;
687
0
        }
688
0
    }
689
690
0
    return poLayer;
691
0
}
692
693
/************************************************************************/
694
/*                 GeoTransformCoordinateTransformation                 */
695
/************************************************************************/
696
697
class GeoTransformCoordinateTransformation final
698
    : public OGRCoordinateTransformation
699
{
700
    const GDALGeoTransform &m_gt;
701
702
  public:
703
    explicit GeoTransformCoordinateTransformation(const GDALGeoTransform &gt)
704
0
        : m_gt(gt)
705
0
    {
706
0
    }
707
708
    const OGRSpatialReference *GetSourceCS() const override;
709
710
    const OGRSpatialReference *GetTargetCS() const override
711
0
    {
712
0
        return nullptr;
713
0
    }
714
715
    OGRCoordinateTransformation *Clone() const override
716
0
    {
717
0
        return new GeoTransformCoordinateTransformation(m_gt);
718
0
    }
719
720
    OGRCoordinateTransformation *GetInverse() const override
721
0
    {
722
0
        CPLError(CE_Failure, CPLE_NotSupported,
723
0
                 "GeoTransformCoordinateTransformation::GetInverse() not "
724
0
                 "implemented");
725
0
        return nullptr;
726
0
    }
727
728
    int Transform(size_t nCount, double *x, double *y, double * /* z */,
729
                  double * /* t */, int *pabSuccess) override
730
0
    {
731
0
        for (size_t i = 0; i < nCount; ++i)
732
0
        {
733
0
            const double X = m_gt.xorig + x[i] * m_gt.xscale + y[i] * m_gt.xrot;
734
0
            const double Y = m_gt.yorig + x[i] * m_gt.yrot + y[i] * m_gt.yscale;
735
0
            x[i] = X;
736
0
            y[i] = Y;
737
0
            if (pabSuccess)
738
0
                pabSuccess[i] = TRUE;
739
0
        }
740
0
        return TRUE;
741
0
    }
742
};
743
744
const OGRSpatialReference *
745
GeoTransformCoordinateTransformation::GetSourceCS() const
746
0
{
747
0
    return nullptr;
748
0
}
749
750
/************************************************************************/
751
/*                            CountPoints()                             */
752
/************************************************************************/
753
754
static size_t CountPoints(const OGRGeometry *poGeom)
755
0
{
756
0
    if (poGeom->getGeometryType() == wkbMultiPolygon)
757
0
    {
758
0
        size_t n = 0;
759
0
        for (auto *poPoly : poGeom->toMultiPolygon())
760
0
        {
761
0
            n += CountPoints(poPoly);
762
0
        }
763
0
        return n;
764
0
    }
765
0
    else if (poGeom->getGeometryType() == wkbPolygon)
766
0
    {
767
0
        size_t n = 0;
768
0
        for (auto *poRing : poGeom->toPolygon())
769
0
        {
770
0
            n += poRing->getNumPoints() - 1;
771
0
        }
772
0
        return n;
773
0
    }
774
0
    return 0;
775
0
}
776
777
/************************************************************************/
778
/*                   GetMinDistanceBetweenTwoPoints()                   */
779
/************************************************************************/
780
781
static double GetMinDistanceBetweenTwoPoints(const OGRGeometry *poGeom)
782
0
{
783
0
    if (poGeom->getGeometryType() == wkbMultiPolygon)
784
0
    {
785
0
        double v = std::numeric_limits<double>::max();
786
0
        for (auto *poPoly : poGeom->toMultiPolygon())
787
0
        {
788
0
            v = std::min(v, GetMinDistanceBetweenTwoPoints(poPoly));
789
0
        }
790
0
        return v;
791
0
    }
792
0
    else if (poGeom->getGeometryType() == wkbPolygon)
793
0
    {
794
0
        double v = std::numeric_limits<double>::max();
795
0
        for (auto *poRing : poGeom->toPolygon())
796
0
        {
797
0
            v = std::min(v, GetMinDistanceBetweenTwoPoints(poRing));
798
0
        }
799
0
        return v;
800
0
    }
801
0
    else if (poGeom->getGeometryType() == wkbLineString)
802
0
    {
803
0
        double v = std::numeric_limits<double>::max();
804
0
        const auto poLS = poGeom->toLineString();
805
0
        const int nNumPoints = poLS->getNumPoints();
806
0
        for (int i = 0; i < nNumPoints - 1; ++i)
807
0
        {
808
0
            const double x1 = poLS->getX(i);
809
0
            const double y1 = poLS->getY(i);
810
0
            const double x2 = poLS->getX(i + 1);
811
0
            const double y2 = poLS->getY(i + 1);
812
0
            const double d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
813
0
            if (d > 0)
814
0
                v = std::min(v, d);
815
0
        }
816
0
        return sqrt(v);
817
0
    }
818
0
    return 0;
819
0
}
820
821
/************************************************************************/
822
/*                        GDALFootprintProcess()                        */
823
/************************************************************************/
824
825
static bool GDALFootprintProcess(GDALDataset *poSrcDS, OGRLayer *poDstLayer,
826
                                 const GDALFootprintOptions *psOptions)
827
0
{
828
0
    std::unique_ptr<OGRCoordinateTransformation> poCT_SRS;
829
0
    const OGRSpatialReference *poDstSRS = poDstLayer->GetSpatialRef();
830
0
    if (!psOptions->oOutputSRS.IsEmpty())
831
0
        poDstSRS = &(psOptions->oOutputSRS);
832
0
    if (poDstSRS)
833
0
    {
834
0
        auto poSrcSRS = poSrcDS->GetSpatialRef();
835
0
        if (!poSrcSRS)
836
0
        {
837
0
            CPLError(CE_Failure, CPLE_AppDefined,
838
0
                     "Output layer has CRS, but input is not georeferenced");
839
0
            return false;
840
0
        }
841
0
        poCT_SRS.reset(OGRCreateCoordinateTransformation(poSrcSRS, poDstSRS));
842
0
        if (!poCT_SRS)
843
0
            return false;
844
0
    }
845
846
0
    std::vector<int> anBands = psOptions->anBands;
847
0
    const int nBandCount = poSrcDS->GetRasterCount();
848
0
    if (anBands.empty())
849
0
    {
850
0
        for (int i = 1; i <= nBandCount; ++i)
851
0
            anBands.push_back(i);
852
0
    }
853
854
0
    std::vector<GDALRasterBand *> apoSrcMaskBands;
855
0
    const CPLStringList aosSrcNoData(
856
0
        CSLTokenizeString2(psOptions->osSrcNoData.c_str(), " ", 0));
857
0
    std::vector<double> adfSrcNoData;
858
0
    if (!psOptions->osSrcNoData.empty())
859
0
    {
860
0
        if (aosSrcNoData.size() != 1 &&
861
0
            static_cast<size_t>(aosSrcNoData.size()) != anBands.size())
862
0
        {
863
0
            CPLError(CE_Failure, CPLE_AppDefined,
864
0
                     "Number of values in -srcnodata should be 1 or the number "
865
0
                     "of bands");
866
0
            return false;
867
0
        }
868
0
        for (int i = 0; i < aosSrcNoData.size(); ++i)
869
0
        {
870
0
            adfSrcNoData.emplace_back(CPLAtof(aosSrcNoData[i]));
871
0
        }
872
0
    }
873
0
    bool bGlobalMask = true;
874
0
    std::vector<std::unique_ptr<GDALRasterBand>> apoTmpNoDataMaskBands;
875
0
    for (size_t i = 0; i < anBands.size(); ++i)
876
0
    {
877
0
        const int nBand = anBands[i];
878
0
        if (nBand <= 0 || nBand > nBandCount)
879
0
        {
880
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid band number: %d",
881
0
                     nBand);
882
0
            return false;
883
0
        }
884
0
        auto poBand = poSrcDS->GetRasterBand(nBand);
885
0
        if (!adfSrcNoData.empty())
886
0
        {
887
0
            bGlobalMask = false;
888
0
            apoTmpNoDataMaskBands.emplace_back(
889
0
                std::make_unique<GDALNoDataMaskBand>(
890
0
                    poBand, adfSrcNoData.size() == 1 ? adfSrcNoData[0]
891
0
                                                     : adfSrcNoData[i]));
892
0
            apoSrcMaskBands.push_back(apoTmpNoDataMaskBands.back().get());
893
0
        }
894
0
        else
895
0
        {
896
0
            GDALRasterBand *poMaskBand;
897
0
            const int nMaskFlags = poBand->GetMaskFlags();
898
0
            if (poBand->GetColorInterpretation() == GCI_AlphaBand)
899
0
            {
900
0
                poMaskBand = poBand;
901
0
            }
902
0
            else
903
0
            {
904
0
                if ((nMaskFlags & GMF_PER_DATASET) == 0)
905
0
                {
906
0
                    bGlobalMask = false;
907
0
                }
908
0
                poMaskBand = poBand->GetMaskBand();
909
0
            }
910
0
            if (psOptions->nOvrIndex >= 0)
911
0
            {
912
0
                if (nMaskFlags == GMF_NODATA)
913
0
                {
914
                    // If the mask band is based on nodata, we don't need
915
                    // to check the overviews of the mask band, but we
916
                    // can take the mask band of the overviews
917
0
                    auto poOvrBand = poBand->GetOverview(psOptions->nOvrIndex);
918
0
                    if (!poOvrBand)
919
0
                    {
920
0
                        if (poBand->GetOverviewCount() == 0)
921
0
                        {
922
0
                            CPLError(
923
0
                                CE_Failure, CPLE_AppDefined,
924
0
                                "Overview index %d invalid for this dataset. "
925
0
                                "Bands of this dataset have no "
926
0
                                "precomputed overviews",
927
0
                                psOptions->nOvrIndex);
928
0
                        }
929
0
                        else
930
0
                        {
931
0
                            CPLError(
932
0
                                CE_Failure, CPLE_AppDefined,
933
0
                                "Overview index %d invalid for this dataset. "
934
0
                                "Value should be in [0,%d] range",
935
0
                                psOptions->nOvrIndex,
936
0
                                poBand->GetOverviewCount() - 1);
937
0
                        }
938
0
                        return false;
939
0
                    }
940
0
                    if (poOvrBand->GetMaskFlags() != GMF_NODATA)
941
0
                    {
942
0
                        CPLError(CE_Failure, CPLE_AppDefined,
943
0
                                 "poOvrBand->GetMaskFlags() != GMF_NODATA");
944
0
                        return false;
945
0
                    }
946
0
                    poMaskBand = poOvrBand->GetMaskBand();
947
0
                }
948
0
                else
949
0
                {
950
0
                    poMaskBand = poMaskBand->GetOverview(psOptions->nOvrIndex);
951
0
                    if (!poMaskBand)
952
0
                    {
953
0
                        if (poBand->GetMaskBand()->GetOverviewCount() == 0)
954
0
                        {
955
0
                            CPLError(
956
0
                                CE_Failure, CPLE_AppDefined,
957
0
                                "Overview index %d invalid for this dataset. "
958
0
                                "Mask bands of this dataset have no "
959
0
                                "precomputed overviews",
960
0
                                psOptions->nOvrIndex);
961
0
                        }
962
0
                        else
963
0
                        {
964
0
                            CPLError(
965
0
                                CE_Failure, CPLE_AppDefined,
966
0
                                "Overview index %d invalid for this dataset. "
967
0
                                "Value should be in [0,%d] range",
968
0
                                psOptions->nOvrIndex,
969
0
                                poBand->GetMaskBand()->GetOverviewCount() - 1);
970
0
                        }
971
0
                        return false;
972
0
                    }
973
0
                }
974
0
            }
975
0
            apoSrcMaskBands.push_back(poMaskBand);
976
0
        }
977
0
    }
978
979
0
    std::unique_ptr<OGRCoordinateTransformation> poCT_GT;
980
0
    GDALGeoTransform gt;
981
0
    if (psOptions->bOutCSGeoref && poSrcDS->GetGeoTransform(gt) == CE_None)
982
0
    {
983
0
        auto poMaskBand = apoSrcMaskBands[0];
984
0
        gt.Rescale(double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize(),
985
0
                   double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize());
986
0
        poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(gt);
987
0
    }
988
0
    else if (psOptions->bOutCSGeorefRequested)
989
0
    {
990
0
        CPLError(CE_Failure, CPLE_AppDefined,
991
0
                 "Georeferenced coordinates requested, but "
992
0
                 "input dataset has no geotransform.");
993
0
        return false;
994
0
    }
995
0
    else if (psOptions->nOvrIndex >= 0)
996
0
    {
997
        // Transform from overview pixel coordinates to full resolution
998
        // pixel coordinates
999
0
        auto poMaskBand = apoSrcMaskBands[0];
1000
0
        gt.xscale = double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize();
1001
0
        gt.xrot = 0;
1002
0
        gt.yrot = 0;
1003
0
        gt.yscale = double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize();
1004
0
        poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(gt);
1005
0
    }
1006
1007
0
    std::unique_ptr<GDALRasterBand> poMaskForRasterize;
1008
0
    if (bGlobalMask || anBands.size() == 1)
1009
0
    {
1010
0
        poMaskForRasterize =
1011
0
            std::make_unique<GDALFootprintMaskBand>(apoSrcMaskBands[0]);
1012
0
    }
1013
0
    else
1014
0
    {
1015
0
        poMaskForRasterize = std::make_unique<GDALFootprintCombinedMaskBand>(
1016
0
            apoSrcMaskBands, psOptions->bCombineBandsUnion);
1017
0
    }
1018
1019
0
    auto hBand = GDALRasterBand::ToHandle(poMaskForRasterize.get());
1020
0
    auto poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
1021
0
    const CPLErr eErr =
1022
0
        GDALPolygonize(hBand, hBand, OGRLayer::ToHandle(poMemLayer.get()),
1023
0
                       /* iPixValField = */ -1,
1024
0
                       /* papszOptions = */ nullptr, psOptions->pfnProgress,
1025
0
                       psOptions->pProgressData);
1026
0
    if (eErr != CE_None)
1027
0
    {
1028
0
        return false;
1029
0
    }
1030
1031
0
    if (!psOptions->bSplitPolys)
1032
0
    {
1033
0
        auto poMP = std::make_unique<OGRMultiPolygon>();
1034
0
        for (auto &&poFeature : poMemLayer.get())
1035
0
        {
1036
0
            auto poGeom =
1037
0
                std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
1038
0
            CPLAssert(poGeom);
1039
0
            if (poGeom->getGeometryType() == wkbPolygon)
1040
0
            {
1041
0
                poMP->addGeometry(std::move(poGeom));
1042
0
            }
1043
0
        }
1044
0
        poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
1045
0
        auto poFeature =
1046
0
            std::make_unique<OGRFeature>(poMemLayer->GetLayerDefn());
1047
0
        poFeature->SetGeometryDirectly(poMP.release());
1048
0
        CPL_IGNORE_RET_VAL(poMemLayer->CreateFeature(std::move(poFeature)));
1049
0
    }
1050
1051
0
    for (auto &&poFeature : poMemLayer.get())
1052
0
    {
1053
0
        auto poGeom = std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
1054
0
        CPLAssert(poGeom);
1055
0
        if (poGeom->IsEmpty())
1056
0
            continue;
1057
1058
0
        auto poDstFeature =
1059
0
            std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
1060
0
        poDstFeature->SetFrom(poFeature.get());
1061
1062
0
        if (poCT_GT)
1063
0
        {
1064
0
            if (poGeom->transform(poCT_GT.get()) != OGRERR_NONE)
1065
0
                return false;
1066
0
        }
1067
1068
0
        if (psOptions->dfDensifyDistance > 0)
1069
0
        {
1070
0
            OGREnvelope sEnvelope;
1071
0
            poGeom->getEnvelope(&sEnvelope);
1072
            // Some sanity check to avoid insane memory allocations
1073
0
            if (sEnvelope.MaxX - sEnvelope.MinX >
1074
0
                    1e6 * psOptions->dfDensifyDistance ||
1075
0
                sEnvelope.MaxY - sEnvelope.MinY >
1076
0
                    1e6 * psOptions->dfDensifyDistance)
1077
0
            {
1078
0
                CPLError(CE_Failure, CPLE_AppDefined,
1079
0
                         "Densification distance too small compared to "
1080
0
                         "geometry extent");
1081
0
                return false;
1082
0
            }
1083
0
            poGeom->segmentize(psOptions->dfDensifyDistance);
1084
0
        }
1085
1086
0
        if (poCT_SRS)
1087
0
        {
1088
0
            if (poGeom->transform(poCT_SRS.get()) != OGRERR_NONE)
1089
0
                return false;
1090
0
        }
1091
1092
0
        if (psOptions->dfMinRingArea != 0)
1093
0
        {
1094
0
            if (poGeom->getGeometryType() == wkbMultiPolygon)
1095
0
            {
1096
0
                auto poMP = std::make_unique<OGRMultiPolygon>();
1097
0
                for (auto *poPoly : poGeom->toMultiPolygon())
1098
0
                {
1099
0
                    auto poNewPoly = std::make_unique<OGRPolygon>();
1100
0
                    for (auto *poRing : poPoly)
1101
0
                    {
1102
0
                        if (poRing->get_Area() >= psOptions->dfMinRingArea)
1103
0
                        {
1104
0
                            poNewPoly->addRing(poRing);
1105
0
                        }
1106
0
                    }
1107
0
                    if (!poNewPoly->IsEmpty())
1108
0
                        poMP->addGeometry(std::move(poNewPoly));
1109
0
                }
1110
0
                poGeom = std::move(poMP);
1111
0
            }
1112
0
            else if (poGeom->getGeometryType() == wkbPolygon)
1113
0
            {
1114
0
                auto poNewPoly = std::make_unique<OGRPolygon>();
1115
0
                for (auto *poRing : poGeom->toPolygon())
1116
0
                {
1117
0
                    if (poRing->get_Area() >= psOptions->dfMinRingArea)
1118
0
                    {
1119
0
                        poNewPoly->addRing(poRing);
1120
0
                    }
1121
0
                }
1122
0
                poGeom = std::move(poNewPoly);
1123
0
            }
1124
0
            if (poGeom->IsEmpty())
1125
0
                continue;
1126
0
        }
1127
1128
0
        const auto GeomIsNullOrEmpty = [&poGeom]
1129
0
        { return !poGeom || poGeom->IsEmpty(); };
1130
1131
0
        if (psOptions->bConvexHull)
1132
0
        {
1133
0
            poGeom.reset(poGeom->ConvexHull());
1134
0
            if (GeomIsNullOrEmpty())
1135
0
                continue;
1136
0
        }
1137
1138
0
        const auto DoSimplification = [&poMemLayer, &poGeom](double dfTolerance)
1139
0
        {
1140
0
            std::string osLastErrorMsg;
1141
0
            {
1142
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1143
0
                CPLErrorReset();
1144
0
                poGeom.reset(poGeom->Simplify(dfTolerance));
1145
0
                osLastErrorMsg = CPLGetLastErrorMsg();
1146
0
            }
1147
0
            if (!poGeom || poGeom->IsEmpty())
1148
0
            {
1149
0
                if (poMemLayer->GetFeatureCount(false) == 1)
1150
0
                {
1151
0
                    if (!osLastErrorMsg.empty())
1152
0
                        CPLError(CE_Failure, CPLE_AppDefined, "%s",
1153
0
                                 osLastErrorMsg.c_str());
1154
0
                    else
1155
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1156
0
                                 "Simplification resulted in empty geometry");
1157
0
                    return false;
1158
0
                }
1159
0
                if (!osLastErrorMsg.empty())
1160
0
                    CPLError(CE_Warning, CPLE_AppDefined, "%s",
1161
0
                             osLastErrorMsg.c_str());
1162
0
            }
1163
0
            return true;
1164
0
        };
1165
1166
0
        if (psOptions->dfSimplifyTolerance != 0)
1167
0
        {
1168
0
            if (!DoSimplification(psOptions->dfSimplifyTolerance))
1169
0
                return false;
1170
0
            if (GeomIsNullOrEmpty())
1171
0
                continue;
1172
0
        }
1173
1174
0
        if (psOptions->nMaxPoints > 0 &&
1175
0
            CountPoints(poGeom.get()) >
1176
0
                static_cast<size_t>(psOptions->nMaxPoints))
1177
0
        {
1178
0
            OGREnvelope sEnvelope;
1179
0
            poGeom->getEnvelope(&sEnvelope);
1180
0
            double tolMin = GetMinDistanceBetweenTwoPoints(poGeom.get());
1181
0
            double tolMax = std::max(sEnvelope.MaxY - sEnvelope.MinY,
1182
0
                                     sEnvelope.MaxX - sEnvelope.MinX);
1183
0
            for (int i = 0; i < 20; ++i)
1184
0
            {
1185
0
                const double tol = (tolMin + tolMax) / 2;
1186
0
                std::unique_ptr<OGRGeometry> poSimplifiedGeom(
1187
0
                    poGeom->Simplify(tol));
1188
0
                if (!poSimplifiedGeom || poSimplifiedGeom->IsEmpty())
1189
0
                {
1190
0
                    tolMax = tol;
1191
0
                    continue;
1192
0
                }
1193
0
                const auto nPoints = CountPoints(poSimplifiedGeom.get());
1194
0
                if (nPoints == static_cast<size_t>(psOptions->nMaxPoints))
1195
0
                {
1196
0
                    tolMax = tol;
1197
0
                    break;
1198
0
                }
1199
0
                else if (nPoints < static_cast<size_t>(psOptions->nMaxPoints))
1200
0
                {
1201
0
                    tolMax = tol;
1202
0
                }
1203
0
                else
1204
0
                {
1205
0
                    tolMin = tol;
1206
0
                }
1207
0
            }
1208
1209
0
            if (!DoSimplification(tolMax))
1210
0
                return false;
1211
0
            if (GeomIsNullOrEmpty())
1212
0
                continue;
1213
0
        }
1214
1215
0
        if (!psOptions->bSplitPolys && poGeom->getGeometryType() == wkbPolygon)
1216
0
            poGeom.reset(
1217
0
                OGRGeometryFactory::forceToMultiPolygon(poGeom.release()));
1218
1219
0
        poDstFeature->SetGeometryDirectly(poGeom.release());
1220
1221
0
        if (!psOptions->osLocationFieldName.empty())
1222
0
        {
1223
1224
0
            std::string osFilename = poSrcDS->GetDescription();
1225
            // Make sure it is a file before building absolute path name.
1226
0
            VSIStatBufL sStatBuf;
1227
0
            if (psOptions->bAbsolutePath &&
1228
0
                CPLIsFilenameRelative(osFilename.c_str()) &&
1229
0
                VSIStatL(osFilename.c_str(), &sStatBuf) == 0)
1230
0
            {
1231
0
                char *pszCurDir = CPLGetCurrentDir();
1232
0
                if (pszCurDir)
1233
0
                {
1234
0
                    osFilename = CPLProjectRelativeFilenameSafe(
1235
0
                        pszCurDir, osFilename.c_str());
1236
0
                    CPLFree(pszCurDir);
1237
0
                }
1238
0
            }
1239
0
            poDstFeature->SetField(psOptions->osLocationFieldName.c_str(),
1240
0
                                   osFilename.c_str());
1241
0
        }
1242
1243
0
        if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
1244
0
        {
1245
0
            return false;
1246
0
        }
1247
0
    }
1248
1249
0
    return true;
1250
0
}
1251
1252
/************************************************************************/
1253
/*                   GDALFootprintAppGetParserUsage()                   */
1254
/************************************************************************/
1255
1256
std::string GDALFootprintAppGetParserUsage()
1257
0
{
1258
0
    try
1259
0
    {
1260
0
        GDALFootprintOptions sOptions;
1261
0
        GDALFootprintOptionsForBinary sOptionsForBinary;
1262
0
        auto argParser =
1263
0
            GDALFootprintAppOptionsGetParser(&sOptions, &sOptionsForBinary);
1264
0
        return argParser->usage();
1265
0
    }
1266
0
    catch (const std::exception &err)
1267
0
    {
1268
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1269
0
                 err.what());
1270
0
        return std::string();
1271
0
    }
1272
0
}
1273
1274
/************************************************************************/
1275
/*                           GDALFootprint()                            */
1276
/************************************************************************/
1277
1278
/* clang-format off */
1279
/**
1280
 * Computes the footprint of a raster.
1281
 *
1282
 * This is the equivalent of the
1283
 * <a href="/programs/gdal_footprint.html">gdal_footprint</a> utility.
1284
 *
1285
 * GDALFootprintOptions* must be allocated and freed with
1286
 * GDALFootprintOptionsNew() and GDALFootprintOptionsFree() respectively.
1287
 * pszDest and hDstDS cannot be used at the same time.
1288
 *
1289
 * @param pszDest the vector destination dataset path or NULL.
1290
 * @param hDstDS the vector destination dataset or NULL.
1291
 * @param hSrcDataset the raster source dataset handle.
1292
 * @param psOptionsIn the options struct returned by GDALFootprintOptionsNew()
1293
 * or NULL.
1294
 * @param pbUsageError pointer to a integer output variable to store if any
1295
 * usage error has occurred or NULL.
1296
 * @return the output dataset (new dataset that must be closed using
1297
 * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
1298
 *
1299
 * @since GDAL 3.8
1300
 */
1301
/* clang-format on */
1302
1303
GDALDatasetH GDALFootprint(const char *pszDest, GDALDatasetH hDstDS,
1304
                           GDALDatasetH hSrcDataset,
1305
                           const GDALFootprintOptions *psOptionsIn,
1306
                           int *pbUsageError)
1307
0
{
1308
0
    if (pszDest == nullptr && hDstDS == nullptr)
1309
0
    {
1310
0
        CPLError(CE_Failure, CPLE_AppDefined,
1311
0
                 "pszDest == NULL && hDstDS == NULL");
1312
1313
0
        if (pbUsageError)
1314
0
            *pbUsageError = TRUE;
1315
0
        return nullptr;
1316
0
    }
1317
0
    if (hSrcDataset == nullptr)
1318
0
    {
1319
0
        CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
1320
1321
0
        if (pbUsageError)
1322
0
            *pbUsageError = TRUE;
1323
0
        return nullptr;
1324
0
    }
1325
0
    if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
1326
0
    {
1327
0
        CPLError(CE_Failure, CPLE_AppDefined,
1328
0
                 "hDstDS != NULL but options that imply creating a new dataset "
1329
0
                 "have been set.");
1330
1331
0
        if (pbUsageError)
1332
0
            *pbUsageError = TRUE;
1333
0
        return nullptr;
1334
0
    }
1335
1336
0
    GDALFootprintOptions *psOptionsToFree = nullptr;
1337
0
    const GDALFootprintOptions *psOptions = psOptionsIn;
1338
0
    if (psOptions == nullptr)
1339
0
    {
1340
0
        psOptionsToFree = GDALFootprintOptionsNew(nullptr, nullptr);
1341
0
        psOptions = psOptionsToFree;
1342
0
    }
1343
1344
0
    const bool bCloseOutDSOnError = hDstDS == nullptr;
1345
1346
0
    auto poSrcDS = GDALDataset::FromHandle(hSrcDataset);
1347
0
    if (poSrcDS->GetRasterCount() == 0)
1348
0
    {
1349
0
        CPLError(CE_Failure, CPLE_AppDefined,
1350
0
                 "Input dataset has no raster band.%s",
1351
0
                 poSrcDS->GetMetadata("SUBDATASETS")
1352
0
                     ? " You need to specify one subdataset."
1353
0
                     : "");
1354
0
        GDALFootprintOptionsFree(psOptionsToFree);
1355
0
        if (bCloseOutDSOnError)
1356
0
            GDALClose(hDstDS);
1357
0
        return nullptr;
1358
0
    }
1359
1360
0
    auto poLayer =
1361
0
        GetOutputLayerAndUpdateDstDS(pszDest, hDstDS, poSrcDS, psOptions);
1362
0
    if (!poLayer)
1363
0
    {
1364
0
        GDALFootprintOptionsFree(psOptionsToFree);
1365
0
        if (hDstDS && bCloseOutDSOnError)
1366
0
            GDALClose(hDstDS);
1367
0
        return nullptr;
1368
0
    }
1369
1370
0
    if (!GDALFootprintProcess(poSrcDS, poLayer, psOptions))
1371
0
    {
1372
0
        GDALFootprintOptionsFree(psOptionsToFree);
1373
0
        if (bCloseOutDSOnError)
1374
0
            GDALClose(hDstDS);
1375
0
        return nullptr;
1376
0
    }
1377
1378
0
    GDALFootprintOptionsFree(psOptionsToFree);
1379
1380
0
    return hDstDS;
1381
0
}
1382
1383
/************************************************************************/
1384
/*                      GDALFootprintOptionsNew()                       */
1385
/************************************************************************/
1386
1387
/**
1388
 * Allocates a GDALFootprintOptions struct.
1389
 *
1390
 * @param papszArgv NULL terminated list of options (potentially including
1391
 * filename and open options too), or NULL. The accepted options are the ones of
1392
 * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
1393
 * @param psOptionsForBinary (output) may be NULL (and should generally be
1394
 * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1395
 * GDALFootprintOptionsForBinaryNew() prior to this function. Will be filled
1396
 * with potentially present filename, open options,...
1397
 * @return pointer to the allocated GDALFootprintOptions struct. Must be freed
1398
 * with GDALFootprintOptionsFree().
1399
 *
1400
 * @since GDAL 3.8
1401
 */
1402
1403
GDALFootprintOptions *
1404
GDALFootprintOptionsNew(char **papszArgv,
1405
                        GDALFootprintOptionsForBinary *psOptionsForBinary)
1406
0
{
1407
0
    auto psOptions = std::make_unique<GDALFootprintOptions>();
1408
1409
    /* -------------------------------------------------------------------- */
1410
    /*      Parse arguments.                                                */
1411
    /* -------------------------------------------------------------------- */
1412
1413
0
    CPLStringList aosArgv;
1414
1415
0
    if (papszArgv)
1416
0
    {
1417
0
        const int nArgc = CSLCount(papszArgv);
1418
0
        for (int i = 0; i < nArgc; i++)
1419
0
        {
1420
0
            aosArgv.AddString(papszArgv[i]);
1421
0
        }
1422
0
    }
1423
1424
0
    try
1425
0
    {
1426
0
        auto argParser = GDALFootprintAppOptionsGetParser(psOptions.get(),
1427
0
                                                          psOptionsForBinary);
1428
1429
0
        argParser->parse_args_without_binary_name(aosArgv.List());
1430
1431
0
        if (argParser->is_used("-t_srs"))
1432
0
        {
1433
1434
0
            const std::string osVal(argParser->get<std::string>("-t_srs"));
1435
0
            if (psOptions->oOutputSRS.SetFromUserInput(osVal.c_str()) !=
1436
0
                OGRERR_NONE)
1437
0
            {
1438
0
                CPLError(CE_Failure, CPLE_AppDefined,
1439
0
                         "Failed to process SRS definition: %s", osVal.c_str());
1440
0
                return nullptr;
1441
0
            }
1442
0
            psOptions->oOutputSRS.SetAxisMappingStrategy(
1443
0
                OAMS_TRADITIONAL_GIS_ORDER);
1444
0
        }
1445
1446
0
        if (argParser->is_used("-max_points"))
1447
0
        {
1448
0
            const std::string maxPoints{
1449
0
                argParser->get<std::string>("-max_points")};
1450
0
            if (maxPoints == "unlimited")
1451
0
            {
1452
0
                psOptions->nMaxPoints = 0;
1453
0
            }
1454
0
            else
1455
0
            {
1456
0
                psOptions->nMaxPoints = atoi(maxPoints.c_str());
1457
0
                if (psOptions->nMaxPoints > 0 && psOptions->nMaxPoints < 3)
1458
0
                {
1459
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1460
0
                             "Invalid value for -max_points");
1461
0
                    return nullptr;
1462
0
                }
1463
0
            }
1464
0
        }
1465
1466
0
        psOptions->bCreateOutput = !psOptions->osFormat.empty();
1467
0
    }
1468
0
    catch (const std::exception &err)
1469
0
    {
1470
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1471
0
                 err.what());
1472
0
        return nullptr;
1473
0
    }
1474
1475
0
    if (!psOptions->bOutCSGeoref && !psOptions->oOutputSRS.IsEmpty())
1476
0
    {
1477
0
        CPLError(CE_Failure, CPLE_AppDefined,
1478
0
                 "-t_cs pixel and -t_srs are mutually exclusive.");
1479
0
        return nullptr;
1480
0
    }
1481
1482
0
    if (psOptions->bClearLocation)
1483
0
    {
1484
0
        psOptions->osLocationFieldName.clear();
1485
0
    }
1486
1487
0
    if (psOptionsForBinary)
1488
0
    {
1489
0
        psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
1490
0
        psOptionsForBinary->osFormat = psOptions->osFormat;
1491
0
        psOptionsForBinary->osDestLayerName = psOptions->osDestLayerName;
1492
0
    }
1493
1494
0
    return psOptions.release();
1495
0
}
1496
1497
/************************************************************************/
1498
/*                      GDALFootprintOptionsFree()                      */
1499
/************************************************************************/
1500
1501
/**
1502
 * Frees the GDALFootprintOptions struct.
1503
 *
1504
 * @param psOptions the options struct for GDALFootprint().
1505
 *
1506
 * @since GDAL 3.8
1507
 */
1508
1509
void GDALFootprintOptionsFree(GDALFootprintOptions *psOptions)
1510
0
{
1511
0
    delete psOptions;
1512
0
}
1513
1514
/************************************************************************/
1515
/*                  GDALFootprintOptionsSetProgress()                   */
1516
/************************************************************************/
1517
1518
/**
1519
 * Set a progress function.
1520
 *
1521
 * @param psOptions the options struct for GDALFootprint().
1522
 * @param pfnProgress the progress callback.
1523
 * @param pProgressData the user data for the progress callback.
1524
 *
1525
 * @since GDAL 3.8
1526
 */
1527
1528
void GDALFootprintOptionsSetProgress(GDALFootprintOptions *psOptions,
1529
                                     GDALProgressFunc pfnProgress,
1530
                                     void *pProgressData)
1531
0
{
1532
0
    psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1533
0
    psOptions->pProgressData = pProgressData;
1534
0
}