Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/polygonize.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  GDAL
3
 * Purpose:  Raster to Polygon Converter
4
 * Author:   Frank Warmerdam, warmerdam@pobox.com
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2008, Frank Warmerdam
8
 * Copyright (c) 2009-2020, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "gdal_alg.h"
15
16
#include <stddef.h>
17
#include <stdio.h>
18
#include <cstdlib>
19
#include <string.h>
20
21
#include <algorithm>
22
#include <limits>
23
#include <map>
24
#include <memory>
25
#include <utility>
26
#include <vector>
27
28
#include "gdal_alg_priv.h"
29
#include "gdal.h"
30
#include "ogr_api.h"
31
#include "ogr_core.h"
32
#include "cpl_conv.h"
33
#include "cpl_error.h"
34
#include "cpl_progress.h"
35
#include "cpl_string.h"
36
#include "cpl_vsi.h"
37
38
#include "polygonize_polygonizer.h"
39
40
using namespace gdal::polygonizer;
41
42
/************************************************************************/
43
/*                          GPMaskImageData()                           */
44
/*                                                                      */
45
/*      Mask out image pixels to a special nodata value if the mask     */
46
/*      band is zero.                                                   */
47
/************************************************************************/
48
49
template <class DataType>
50
static CPLErr GPMaskImageData(GDALRasterBandH hMaskBand, GByte *pabyMaskLine,
51
                              int iY, int nXSize, DataType *panImageLine)
52
53
0
{
54
0
    const CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1,
55
0
                                     pabyMaskLine, nXSize, 1, GDT_UInt8, 0, 0);
56
0
    if (eErr != CE_None)
57
0
        return eErr;
58
59
0
    for (int i = 0; i < nXSize; i++)
60
0
    {
61
0
        if (pabyMaskLine[i] == 0)
62
0
            panImageLine[i] = GP_NODATA_MARKER;
63
0
    }
64
65
0
    return CE_None;
66
0
}
Unexecuted instantiation: polygonize.cpp:CPLErr GPMaskImageData<long>(void*, unsigned char*, int, int, long*)
Unexecuted instantiation: polygonize.cpp:CPLErr GPMaskImageData<double>(void*, unsigned char*, int, int, double*)
Unexecuted instantiation: polygonize.cpp:CPLErr GPMaskImageData<float>(void*, unsigned char*, int, int, float*)
67
68
/************************************************************************/
69
/*                          GDALPolygonizeT()                           */
70
/************************************************************************/
71
72
template <class DataType, class EqualityTest>
73
static CPLErr GDALPolygonizeT(GDALRasterBandH hSrcBand,
74
                              GDALRasterBandH hMaskBand, OGRLayerH hOutLayer,
75
                              int iPixValField, CSLConstList papszOptions,
76
                              GDALProgressFunc pfnProgress, void *pProgressArg,
77
                              GDALDataType eDT)
78
79
0
{
80
0
    VALIDATE_POINTER1(hSrcBand, "GDALPolygonize", CE_Failure);
81
0
    VALIDATE_POINTER1(hOutLayer, "GDALPolygonize", CE_Failure);
82
83
0
    if (pfnProgress == nullptr)
84
0
        pfnProgress = GDALDummyProgress;
85
86
0
    const int nConnectedness =
87
0
        CSLFetchNameValue(papszOptions, "8CONNECTED") ? 8 : 4;
88
89
    /* -------------------------------------------------------------------- */
90
    /*      Confirm our output layer will support feature creation.         */
91
    /* -------------------------------------------------------------------- */
92
0
    if (!OGR_L_TestCapability(hOutLayer, OLCSequentialWrite))
93
0
    {
94
0
        CPLError(CE_Failure, CPLE_AppDefined,
95
0
                 "Output feature layer does not appear to support creation "
96
0
                 "of features in GDALPolygonize().");
97
0
        return CE_Failure;
98
0
    }
99
100
    /* -------------------------------------------------------------------- */
101
    /*      Allocate working buffers.                                       */
102
    /* -------------------------------------------------------------------- */
103
0
    const int nXSize = GDALGetRasterBandXSize(hSrcBand);
104
0
    const int nYSize = GDALGetRasterBandYSize(hSrcBand);
105
0
    if (nXSize > std::numeric_limits<int>::max() - 2)
106
0
    {
107
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too wide raster");
108
0
        return CE_Failure;
109
0
    }
110
111
0
    DataType *panLastLineVal =
112
0
        static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
113
0
    DataType *panThisLineVal =
114
0
        static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
115
0
    GInt32 *panLastLineId =
116
0
        static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
117
0
    GInt32 *panThisLineId =
118
0
        static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
119
120
0
    GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
121
122
0
    if (panLastLineVal == nullptr || panThisLineVal == nullptr ||
123
0
        panLastLineId == nullptr || panThisLineId == nullptr ||
124
0
        pabyMaskLine == nullptr)
125
0
    {
126
0
        CPLFree(panThisLineId);
127
0
        CPLFree(panLastLineId);
128
0
        CPLFree(panThisLineVal);
129
0
        CPLFree(panLastLineVal);
130
0
        CPLFree(pabyMaskLine);
131
0
        return CE_Failure;
132
0
    }
133
134
    /* -------------------------------------------------------------------- */
135
    /*      Get the geotransform, if there is one, so we can convert the    */
136
    /*      vectors into georeferenced coordinates.                         */
137
    /* -------------------------------------------------------------------- */
138
0
    GDALGeoTransform gt;
139
0
    bool bGotGeoTransform = false;
140
0
    const char *pszDatasetForGeoRef =
141
0
        CSLFetchNameValue(papszOptions, "DATASET_FOR_GEOREF");
142
0
    if (pszDatasetForGeoRef)
143
0
    {
144
0
        auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
145
0
            pszDatasetForGeoRef, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
146
0
        if (poSrcDS)
147
0
        {
148
0
            bGotGeoTransform = poSrcDS->GetGeoTransform(gt) == CE_None;
149
0
        }
150
0
    }
151
0
    else
152
0
    {
153
0
        auto poSrcDS = GDALRasterBand::FromHandle(hSrcBand)->GetDataset();
154
0
        if (poSrcDS)
155
0
        {
156
0
            bGotGeoTransform = poSrcDS->GetGeoTransform(gt) == CE_None;
157
0
        }
158
0
    }
159
0
    if (!bGotGeoTransform)
160
0
    {
161
0
        gt = GDALGeoTransform();
162
0
    }
163
164
    /* -------------------------------------------------------------------- */
165
    /*      The first pass over the raster is only used to build up the     */
166
    /*      polygon id map so we will know in advance what polygons are     */
167
    /*      what on the second pass.                                        */
168
    /* -------------------------------------------------------------------- */
169
0
    GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oFirstEnum(
170
0
        nConnectedness);
171
172
0
    CPLErr eErr = CE_None;
173
174
0
    for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
175
0
    {
176
0
        eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
177
0
                            nXSize, 1, eDT, 0, 0);
178
179
0
        if (eErr == CE_None && hMaskBand != nullptr)
180
0
            eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
181
0
                                   panThisLineVal);
182
183
0
        if (eErr != CE_None)
184
0
            break;
185
186
0
        if (iY == 0)
187
0
            eErr = oFirstEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
188
0
                                          panThisLineId, nXSize)
189
0
                       ? CE_None
190
0
                       : CE_Failure;
191
0
        else
192
0
            eErr = oFirstEnum.ProcessLine(panLastLineVal, panThisLineVal,
193
0
                                          panLastLineId, panThisLineId, nXSize)
194
0
                       ? CE_None
195
0
                       : CE_Failure;
196
197
0
        if (eErr != CE_None)
198
0
            break;
199
200
        // Swap lines.
201
0
        std::swap(panLastLineVal, panThisLineVal);
202
0
        std::swap(panLastLineId, panThisLineId);
203
204
        /* --------------------------------------------------------------------
205
         */
206
        /*      Report progress, and support interrupts. */
207
        /* --------------------------------------------------------------------
208
         */
209
0
        if (!pfnProgress(0.10 * ((iY + 1) / static_cast<double>(nYSize)), "",
210
0
                         pProgressArg))
211
0
        {
212
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
213
0
            eErr = CE_Failure;
214
0
        }
215
0
    }
216
217
    /* -------------------------------------------------------------------- */
218
    /*      Make a pass through the maps, ensuring every polygon id         */
219
    /*      points to the final id it should use, not an intermediate       */
220
    /*      value.                                                          */
221
    /* -------------------------------------------------------------------- */
222
0
    if (eErr == CE_None)
223
0
        oFirstEnum.CompleteMerges();
224
225
    /* -------------------------------------------------------------------- */
226
    /*      We will use a new enumerator for the second pass primarily      */
227
    /*      so we can preserve the first pass map.                          */
228
    /* -------------------------------------------------------------------- */
229
0
    GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oSecondEnum(
230
0
        nConnectedness);
231
232
0
    OGRPolygonWriter<DataType> oPolygonWriter{
233
0
        hOutLayer, iPixValField, gt,
234
0
        atoi(CSLFetchNameValueDef(papszOptions, "COMMIT_INTERVAL", "100000"))};
235
0
    Polygonizer<GInt32, DataType> oPolygonizer{-1, &oPolygonWriter};
236
0
    TwoArm *paoLastLineArm =
237
0
        static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
238
0
    TwoArm *paoThisLineArm =
239
0
        static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
240
241
0
    if (paoThisLineArm == nullptr || paoLastLineArm == nullptr)
242
0
    {
243
0
        eErr = CE_Failure;
244
0
    }
245
0
    else
246
0
    {
247
0
        for (int i = 0; i < nXSize + 2; ++i)
248
0
        {
249
0
            paoLastLineArm[i].poPolyInside = oPolygonizer.getTheOuterPolygon();
250
0
        }
251
0
    }
252
253
    /* ==================================================================== */
254
    /*      Second pass during which we will actually collect polygon       */
255
    /*      edges as geometries.                                            */
256
    /* ==================================================================== */
257
0
    for (int iY = 0; eErr == CE_None && iY < nYSize + 1; iY++)
258
0
    {
259
        /* --------------------------------------------------------------------
260
         */
261
        /*      Read the image data. */
262
        /* --------------------------------------------------------------------
263
         */
264
0
        if (iY < nYSize)
265
0
        {
266
0
            eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1,
267
0
                                panThisLineVal, nXSize, 1, eDT, 0, 0);
268
0
            if (eErr == CE_None && hMaskBand != nullptr)
269
0
                eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
270
0
                                       panThisLineVal);
271
0
        }
272
273
0
        if (eErr != CE_None)
274
0
            continue;
275
276
        /* --------------------------------------------------------------------
277
         */
278
        /*      Determine what polygon the various pixels belong to (redoing */
279
        /*      the same thing done in the first pass above). */
280
        /* --------------------------------------------------------------------
281
         */
282
0
        if (iY == nYSize)
283
0
        {
284
0
            for (int iX = 0; iX < nXSize; iX++)
285
0
                panThisLineId[iX] =
286
0
                    decltype(oPolygonizer)::THE_OUTER_POLYGON_ID;
287
0
        }
288
0
        else if (iY == 0)
289
0
        {
290
0
            eErr = oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
291
0
                                           panThisLineId, nXSize)
292
0
                       ? CE_None
293
0
                       : CE_Failure;
294
0
        }
295
0
        else
296
0
        {
297
0
            eErr = oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
298
0
                                           panLastLineId, panThisLineId, nXSize)
299
0
                       ? CE_None
300
0
                       : CE_Failure;
301
0
        }
302
303
0
        if (eErr != CE_None)
304
0
            continue;
305
306
0
        if (iY < nYSize)
307
0
        {
308
0
            for (int iX = 0; iX < nXSize; iX++)
309
0
            {
310
                // TODO: maybe we can reserve -1 as the lookup result for -1 polygon id in the panPolyIdMap,
311
                //       so the this expression becomes: panLastLineId[iX] = *(oFirstEnum.panPolyIdMap + panThisLineId[iX]).
312
                //       This would eliminate the condition checking.
313
0
                panLastLineId[iX] =
314
0
                    panThisLineId[iX] == -1
315
0
                        ? -1
316
0
                        : oFirstEnum.panPolyIdMap[panThisLineId[iX]];
317
0
            }
318
319
0
            if (!oPolygonizer.processLine(panLastLineId, panLastLineVal,
320
0
                                          paoThisLineArm, paoLastLineArm, iY,
321
0
                                          nXSize))
322
0
            {
323
0
                eErr = CE_Failure;
324
0
            }
325
0
            else
326
0
            {
327
0
                eErr = oPolygonWriter.getErr();
328
0
            }
329
0
        }
330
0
        else
331
0
        {
332
0
            if (!oPolygonizer.processLine(panThisLineId, panLastLineVal,
333
0
                                          paoThisLineArm, paoLastLineArm, iY,
334
0
                                          nXSize))
335
0
            {
336
0
                eErr = CE_Failure;
337
0
            }
338
0
            else
339
0
            {
340
0
                eErr = oPolygonWriter.getErr();
341
0
            }
342
0
        }
343
344
0
        if (eErr != CE_None)
345
0
            continue;
346
347
        /* --------------------------------------------------------------------
348
         */
349
        /*      Swap pixel value, and polygon id lines to be ready for the */
350
        /*      next line. */
351
        /* --------------------------------------------------------------------
352
         */
353
0
        std::swap(panLastLineVal, panThisLineVal);
354
0
        std::swap(panLastLineId, panThisLineId);
355
0
        std::swap(paoThisLineArm, paoLastLineArm);
356
357
        /* --------------------------------------------------------------------
358
         */
359
        /*      Report progress, and support interrupts. */
360
        /* --------------------------------------------------------------------
361
         */
362
0
        if (!pfnProgress(
363
0
                std::min(1.0, 0.10 + 0.90 * ((iY + 1) /
364
0
                                             static_cast<double>(nYSize))),
365
0
                "", pProgressArg))
366
0
        {
367
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
368
0
            eErr = CE_Failure;
369
0
        }
370
0
    }
371
372
0
    if (!oPolygonWriter.Finalize())
373
0
        eErr = CE_Failure;
374
375
    /* -------------------------------------------------------------------- */
376
    /*      Cleanup                                                         */
377
    /* -------------------------------------------------------------------- */
378
0
    CPLFree(panThisLineId);
379
0
    CPLFree(panLastLineId);
380
0
    CPLFree(panThisLineVal);
381
0
    CPLFree(panLastLineVal);
382
0
    CPLFree(paoThisLineArm);
383
0
    CPLFree(paoLastLineArm);
384
0
    CPLFree(pabyMaskLine);
385
386
0
    return eErr;
387
0
}
Unexecuted instantiation: polygonize.cpp:CPLErr GDALPolygonizeT<long, IntEqualityTest>(void*, void*, OGRLayerHS*, int, char const* const*, int (*)(double, char const*, void*), void*, GDALDataType)
Unexecuted instantiation: polygonize.cpp:CPLErr GDALPolygonizeT<double, DoubleEqualityTest>(void*, void*, OGRLayerHS*, int, char const* const*, int (*)(double, char const*, void*), void*, GDALDataType)
Unexecuted instantiation: polygonize.cpp:CPLErr GDALPolygonizeT<float, FloatEqualityTest>(void*, void*, OGRLayerHS*, int, char const* const*, int (*)(double, char const*, void*), void*, GDALDataType)
388
389
/******************************************************************************/
390
/*                       GDALFloatAlmostEquals()                              */
391
/* Code (originally) from:                                                    */
392
/* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm  */
393
/******************************************************************************/
394
395
template <typename FloatType, typename IntType>
396
static inline bool GDALFloatAlmostEquals(FloatType A, FloatType B,
397
                                         unsigned maxUlps)
398
0
{
399
0
    static_assert(sizeof(FloatType) == sizeof(IntType));
400
    // This function will allow maxUlps-1 floats between A and B.
401
402
    // Make sure maxUlps is non-negative and small enough that the default NAN
403
    // won't compare as equal to anything.
404
0
    if (maxUlps >= 4 * 1024 * 1024)
405
0
    {
406
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Invalid maxUlps");
407
0
        return false;
408
0
    }
409
410
0
    const auto MapToInteger = [](FloatType x)
411
0
    {
412
0
        IntType i = 0;
413
0
        memcpy(&i, &x, sizeof(i));
414
415
0
        constexpr int NBITS = 8 * static_cast<int>(sizeof(i)) - 1;
416
0
        constexpr IntType SHIFT = (static_cast<IntType>(1) << NBITS);
417
418
        // Make i lexicographically ordered with negative values
419
        // remapped to the [0, SHIFT[ range and positive values in the
420
        // [SHIFT, UINT_MAX[ range
421
0
        if ((i >> NBITS) != 0)
422
0
            i = SHIFT - (i & ~SHIFT);
423
0
        else
424
0
            i += SHIFT;
425
0
        return i;
426
0
    };
Unexecuted instantiation: polygonize.cpp:GDALFloatAlmostEquals<float, unsigned int>(float, float, unsigned int)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: polygonize.cpp:GDALFloatAlmostEquals<double, unsigned long>(double, double, unsigned int)::{lambda(double)#1}::operator()(double) const
427
428
0
    const auto aInt = MapToInteger(A);
429
0
    const auto bInt = MapToInteger(B);
430
431
0
    return ((aInt > bInt) ? aInt - bInt : bInt - aInt) <= maxUlps;
432
0
}
Unexecuted instantiation: polygonize.cpp:bool GDALFloatAlmostEquals<float, unsigned int>(float, float, unsigned int)
Unexecuted instantiation: polygonize.cpp:bool GDALFloatAlmostEquals<double, unsigned long>(double, double, unsigned int)
433
434
bool GDALFloatAlmostEquals(float A, float B, unsigned maxUlps)
435
0
{
436
0
    return GDALFloatAlmostEquals<float, uint32_t>(A, B, maxUlps);
437
0
}
438
439
/************************************************************************/
440
/*                       GDALDoubleAlmostEquals()                       */
441
/************************************************************************/
442
443
bool GDALDoubleAlmostEquals(double A, double B, unsigned maxUlps)
444
0
{
445
0
    return GDALFloatAlmostEquals<double, uint64_t>(A, B, maxUlps);
446
0
}
447
448
/************************************************************************/
449
/*                           GDALPolygonize()                           */
450
/************************************************************************/
451
452
/**
453
 * Create polygon coverage from raster data.
454
 *
455
 * This function creates vector polygons for all connected regions of pixels in
456
 * the raster sharing a common pixel value.  Optionally each polygon may be
457
 * labeled with the pixel value in an attribute.  Optionally a mask band
458
 * can be provided to determine which pixels are eligible for processing.
459
 *
460
 * Note that currently the source pixel band values are read into a
461
 * signed 64bit integer buffer (Int64), so floating point or complex
462
 * bands will be implicitly truncated before processing. If you want to use a
463
 * version using 32bit or 64bit float buffers, see GDALFPolygonize().
464
 *
465
 * Polygon features will be created on the output layer, with polygon
466
 * geometries representing the polygons.  The polygon geometries will be
467
 * in the georeferenced coordinate system of the image (based on the
468
 * geotransform of the source dataset).  It is acceptable for the output
469
 * layer to already have features.  Note that GDALPolygonize() does not
470
 * set the coordinate system on the output layer.  Application code should
471
 * do this when the layer is created, presumably matching the raster
472
 * coordinate system.
473
 *
474
 * The algorithm used attempts to minimize memory use so that very large
475
 * rasters can be processed.  However, if the raster has many polygons
476
 * or very large/complex polygons, the memory use for holding polygon
477
 * enumerations and active polygon geometries may grow to be quite large.
478
 *
479
 * The algorithm will generally produce very dense polygon geometries, with
480
 * edges that follow exactly on pixel boundaries for all non-interior pixels.
481
 * For non-thematic raster data (such as satellite images) the result will
482
 * essentially be one small polygon per pixel, and memory and output layer
483
 * sizes will be substantial.  The algorithm is primarily intended for
484
 * relatively simple thematic imagery, masks, and classification results.
485
 *
486
 * @param hSrcBand the source raster band to be processed.
487
 * @param hMaskBand an optional mask band.  All pixels in the mask band with a
488
 * value other than zero will be considered suitable for collection as
489
 * polygons.
490
 * @param hOutLayer the vector feature layer to which the polygons should
491
 * be written.
492
 * @param iPixValField the attribute field index indicating the feature
493
 * attribute into which the pixel value of the polygon should be written. Or
494
 * -1 to indicate that the pixel value must not be written.
495
 * @param papszOptions a name/value list of additional options
496
 * <ul>
497
 * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
498
 * Otherwise 4 connectedness will be applied to the algorithm</li>
499
 * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
500
 * the geotransform. This useful if hSrcBand has no related dataset, which is
501
 * typical for mask bands.</li>
502
 * <li>COMMIT_INTERVAL=num:
503
 * (GDAL >= 3.12) Interval in number of features at which transactions must be
504
 * flushed. A value of 0 means that no transactions are opened.
505
 * A negative value means a single transaction.
506
 * The default value is 100000.
507
 * The function takes care of issuing the starting transaction and committing
508
 * the final one.
509
 * </li>
510
 * </ul>
511
 * @param pfnProgress callback for reporting algorithm progress matching the
512
 * GDALProgressFunc() semantics.  May be NULL.
513
 * @param pProgressArg callback argument passed to pfnProgress.
514
 *
515
 * @return CE_None on success or CE_Failure on a failure.
516
 */
517
518
CPLErr CPL_STDCALL GDALPolygonize(GDALRasterBandH hSrcBand,
519
                                  GDALRasterBandH hMaskBand,
520
                                  OGRLayerH hOutLayer, int iPixValField,
521
                                  char **papszOptions,
522
                                  GDALProgressFunc pfnProgress,
523
                                  void *pProgressArg)
524
525
0
{
526
0
    return GDALPolygonizeT<std::int64_t, IntEqualityTest>(
527
0
        hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions, pfnProgress,
528
0
        pProgressArg, GDT_Int64);
529
0
}
530
531
/************************************************************************/
532
/*                          GDALFPolygonize()                           */
533
/************************************************************************/
534
535
/**
536
 * Create polygon coverage from raster data.
537
 *
538
 * This function creates vector polygons for all connected regions of pixels in
539
 * the raster sharing a common pixel value.  Optionally each polygon may be
540
 * labeled with the pixel value in an attribute.  Optionally a mask band
541
 * can be provided to determine which pixels are eligible for processing.
542
 *
543
 * The source pixel band values are read into a 32-bit float buffer, or 64-bit
544
 * float if the source band is 64-bit float itself. If you want
545
 * to use a (probably faster) version using signed 32bit integer buffer, see
546
 * GDALPolygonize().
547
 *
548
 * Polygon features will be created on the output layer, with polygon
549
 * geometries representing the polygons.  The polygon geometries will be
550
 * in the georeferenced coordinate system of the image (based on the
551
 * geotransform of the source dataset).  It is acceptable for the output
552
 * layer to already have features.  Note that GDALFPolygonize() does not
553
 * set the coordinate system on the output layer.  Application code should
554
 * do this when the layer is created, presumably matching the raster
555
 * coordinate system.
556
 *
557
 * The algorithm used attempts to minimize memory use so that very large
558
 * rasters can be processed.  However, if the raster has many polygons
559
 * or very large/complex polygons, the memory use for holding polygon
560
 * enumerations and active polygon geometries may grow to be quite large.
561
 *
562
 * The algorithm will generally produce very dense polygon geometries, with
563
 * edges that follow exactly on pixel boundaries for all non-interior pixels.
564
 * For non-thematic raster data (such as satellite images) the result will
565
 * essentially be one small polygon per pixel, and memory and output layer
566
 * sizes will be substantial.  The algorithm is primarily intended for
567
 * relatively simple thematic imagery, masks, and classification results.
568
 *
569
 * @param hSrcBand the source raster band to be processed.
570
 * @param hMaskBand an optional mask band.  All pixels in the mask band with a
571
 * value other than zero will be considered suitable for collection as
572
 * polygons.
573
 * @param hOutLayer the vector feature layer to which the polygons should
574
 * be written.
575
 * @param iPixValField the attribute field index indicating the feature
576
 * attribute into which the pixel value of the polygon should be written. Or
577
 * -1 to indicate that the pixel value must not be written.
578
 * @param papszOptions a name/value list of additional options
579
 * <ul>
580
 * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
581
 * Otherwise 4 connectedness will be applied to the algorithm</li>
582
 * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
583
 * the geotransform. This useful if hSrcBand has no related dataset, which is
584
 * typical for mask bands.</li>
585
 * <li>COMMIT_INTERVAL=num:
586
 * (GDAL >= 3.12) Interval in number of features at which transactions must be
587
 * flushed. A value of 0 means that no transactions are opened.
588
 * A negative value means a single transaction.
589
 * The default value is 100000.
590
 * The function takes care of issuing the starting transaction and committing
591
 * the final one.
592
 * </li>
593
 * </ul>
594
 * @param pfnProgress callback for reporting algorithm progress matching the
595
 * GDALProgressFunc() semantics.  May be NULL.
596
 * @param pProgressArg callback argument passed to pfnProgress.
597
 *
598
 * @return CE_None on success or CE_Failure on a failure.
599
 *
600
 */
601
602
CPLErr CPL_STDCALL GDALFPolygonize(GDALRasterBandH hSrcBand,
603
                                   GDALRasterBandH hMaskBand,
604
                                   OGRLayerH hOutLayer, int iPixValField,
605
                                   char **papszOptions,
606
                                   GDALProgressFunc pfnProgress,
607
                                   void *pProgressArg)
608
609
0
{
610
0
    if (GDALGetRasterDataType(hSrcBand) == GDT_Float64)
611
0
    {
612
0
        return GDALPolygonizeT<double, DoubleEqualityTest>(
613
0
            hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions,
614
0
            pfnProgress, pProgressArg, GDT_Float64);
615
0
    }
616
0
    else
617
0
    {
618
0
        return GDALPolygonizeT<float, FloatEqualityTest>(
619
0
            hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions,
620
0
            pfnProgress, pProgressArg, GDT_Float32);
621
0
    }
622
0
}