Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdalrasterpolygonenumerator.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Raster Polygon Enumerator
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2008, Frank Warmerdam
9
 * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gdal_alg_priv.h"
16
17
#include <cstddef>
18
#include <limits>
19
20
#include "cpl_conv.h"
21
#include "cpl_error.h"
22
23
/*! @cond Doxygen_Suppress */
24
25
/************************************************************************/
26
/*                    GDALRasterPolygonEnumeratorT()                    */
27
/************************************************************************/
28
29
template <class DataType, class EqualityTest>
30
GDALRasterPolygonEnumeratorT<
31
    DataType, EqualityTest>::GDALRasterPolygonEnumeratorT(int nConnectednessIn)
32
0
    : nConnectedness(nConnectednessIn)
33
34
0
{
35
0
    CPLAssert(nConnectedness == 4 || nConnectedness == 8);
36
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::GDALRasterPolygonEnumeratorT(int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::GDALRasterPolygonEnumeratorT(int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::GDALRasterPolygonEnumeratorT(int)
37
38
/************************************************************************/
39
/*                   ~GDALRasterPolygonEnumeratorT()                    */
40
/************************************************************************/
41
42
template <class DataType, class EqualityTest>
43
GDALRasterPolygonEnumeratorT<DataType,
44
                             EqualityTest>::~GDALRasterPolygonEnumeratorT()
45
46
0
{
47
0
    Clear();
48
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::~GDALRasterPolygonEnumeratorT()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::~GDALRasterPolygonEnumeratorT()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::~GDALRasterPolygonEnumeratorT()
49
50
/************************************************************************/
51
/*                               Clear()                                */
52
/************************************************************************/
53
54
template <class DataType, class EqualityTest>
55
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::Clear()
56
57
0
{
58
0
    CPLFree(panPolyIdMap);
59
0
    CPLFree(panPolyValue);
60
61
0
    panPolyIdMap = nullptr;
62
0
    panPolyValue = nullptr;
63
64
0
    nNextPolygonId = 0;
65
0
    nPolyAlloc = 0;
66
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::Clear()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::Clear()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::Clear()
67
68
/************************************************************************/
69
/*                            MergePolygon()                            */
70
/*                                                                      */
71
/*      Update the polygon map to indicate the merger of two polygons.  */
72
/************************************************************************/
73
74
template <class DataType, class EqualityTest>
75
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::MergePolygon(
76
    int nSrcId, int nDstIdInit)
77
78
0
{
79
    // Figure out the final dest id.
80
0
    int nDstIdFinal = nDstIdInit;
81
0
    while (panPolyIdMap[nDstIdFinal] != nDstIdFinal)
82
0
        nDstIdFinal = panPolyIdMap[nDstIdFinal];
83
84
    // Map the whole intermediate chain to it.
85
0
    int nDstIdCur = nDstIdInit;
86
0
    while (panPolyIdMap[nDstIdCur] != nDstIdCur)
87
0
    {
88
0
        int nNextDstId = panPolyIdMap[nDstIdCur];
89
0
        panPolyIdMap[nDstIdCur] = nDstIdFinal;
90
0
        nDstIdCur = nNextDstId;
91
0
    }
92
93
    // And map the whole source chain to it too (can be done in one pass).
94
0
    while (panPolyIdMap[nSrcId] != nSrcId)
95
0
    {
96
0
        int nNextSrcId = panPolyIdMap[nSrcId];
97
0
        panPolyIdMap[nSrcId] = nDstIdFinal;
98
0
        nSrcId = nNextSrcId;
99
0
    }
100
0
    panPolyIdMap[nSrcId] = nDstIdFinal;
101
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::MergePolygon(int, int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::MergePolygon(int, int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::MergePolygon(int, int)
102
103
/************************************************************************/
104
/*                             NewPolygon()                             */
105
/*                                                                      */
106
/*      Allocate a new polygon id, and reallocate the polygon maps      */
107
/*      if needed.                                                      */
108
/************************************************************************/
109
110
template <class DataType, class EqualityTest>
111
int GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::NewPolygon(
112
    DataType nValue)
113
114
0
{
115
0
    if (nNextPolygonId == std::numeric_limits<int>::max())
116
0
    {
117
0
        CPLError(CE_Failure, CPLE_AppDefined,
118
0
                 "GDALRasterPolygonEnumeratorT::NewPolygon(): maximum number "
119
0
                 "of polygons reached");
120
0
        return -1;
121
0
    }
122
0
    if (nNextPolygonId >= nPolyAlloc)
123
0
    {
124
0
        int nPolyAllocNew;
125
0
        if (nPolyAlloc < (std::numeric_limits<int>::max() - 20) / 2)
126
0
            nPolyAllocNew = nPolyAlloc * 2 + 20;
127
0
        else
128
0
            nPolyAllocNew = std::numeric_limits<int>::max();
129
#if SIZEOF_VOIDP == 4
130
        if (nPolyAllocNew >
131
                static_cast<int>(std::numeric_limits<size_t>::max() /
132
                                 sizeof(GInt32)) ||
133
            nPolyAllocNew >
134
                static_cast<int>(std::numeric_limits<size_t>::max() /
135
                                 sizeof(DataType)))
136
        {
137
            CPLError(CE_Failure, CPLE_OutOfMemory,
138
                     "GDALRasterPolygonEnumeratorT::NewPolygon(): too many "
139
                     "polygons");
140
            return -1;
141
        }
142
#endif
143
0
        auto panPolyIdMapNew = static_cast<GInt32 *>(
144
0
            VSI_REALLOC_VERBOSE(panPolyIdMap, nPolyAllocNew * sizeof(GInt32)));
145
0
        auto panPolyValueNew = static_cast<DataType *>(VSI_REALLOC_VERBOSE(
146
0
            panPolyValue, nPolyAllocNew * sizeof(DataType)));
147
0
        if (panPolyIdMapNew == nullptr || panPolyValueNew == nullptr)
148
0
        {
149
0
            VSIFree(panPolyIdMapNew);
150
0
            VSIFree(panPolyValueNew);
151
0
            return -1;
152
0
        }
153
0
        panPolyIdMap = panPolyIdMapNew;
154
0
        panPolyValue = panPolyValueNew;
155
0
        nPolyAlloc = nPolyAllocNew;
156
0
    }
157
158
0
    const int nPolyId = nNextPolygonId;
159
0
    panPolyIdMap[nPolyId] = nPolyId;
160
0
    panPolyValue[nPolyId] = nValue;
161
0
    nNextPolygonId++;
162
163
0
    return nPolyId;
164
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::NewPolygon(long)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::NewPolygon(float)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::NewPolygon(double)
165
166
/************************************************************************/
167
/*                           CompleteMerges()                           */
168
/*                                                                      */
169
/*      Make a pass through the maps, ensuring every polygon id         */
170
/*      points to the final id it should use, not an intermediate       */
171
/*      value.                                                          */
172
/************************************************************************/
173
174
template <class DataType, class EqualityTest>
175
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::CompleteMerges()
176
177
0
{
178
0
    int nFinalPolyCount = 0;
179
180
0
    for (int iPoly = 0; iPoly < nNextPolygonId; iPoly++)
181
0
    {
182
        // Figure out the final id.
183
0
        int nId = panPolyIdMap[iPoly];
184
0
        while (nId != panPolyIdMap[nId])
185
0
        {
186
0
            nId = panPolyIdMap[nId];
187
0
        }
188
189
        // Then map the whole intermediate chain to it.
190
0
        int nIdCur = panPolyIdMap[iPoly];
191
0
        panPolyIdMap[iPoly] = nId;
192
0
        while (nIdCur != panPolyIdMap[nIdCur])
193
0
        {
194
0
            int nNextId = panPolyIdMap[nIdCur];
195
0
            panPolyIdMap[nIdCur] = nId;
196
0
            nIdCur = nNextId;
197
0
        }
198
199
0
        if (panPolyIdMap[iPoly] == iPoly)
200
0
            nFinalPolyCount++;
201
0
    }
202
203
0
    CPLDebug("GDALRasterPolygonEnumerator",
204
0
             "Counted %d polygon fragments forming %d final polygons.",
205
0
             nNextPolygonId, nFinalPolyCount);
206
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::CompleteMerges()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::CompleteMerges()
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::CompleteMerges()
207
208
/************************************************************************/
209
/*                            ProcessLine()                             */
210
/*                                                                      */
211
/*      Assign ids to polygons, one line at a time.                     */
212
/************************************************************************/
213
214
template <class DataType, class EqualityTest>
215
bool GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::ProcessLine(
216
    DataType *panLastLineVal, DataType *panThisLineVal, GInt32 *panLastLineId,
217
    GInt32 *panThisLineId, int nXSize)
218
219
0
{
220
0
    EqualityTest eq;
221
222
    /* -------------------------------------------------------------------- */
223
    /*      Special case for the first line.                                */
224
    /* -------------------------------------------------------------------- */
225
0
    if (panLastLineVal == nullptr)
226
0
    {
227
0
        for (int i = 0; i < nXSize; i++)
228
0
        {
229
0
            if (panThisLineVal[i] == GP_NODATA_MARKER)
230
0
            {
231
0
                panThisLineId[i] = -1;
232
0
            }
233
0
            else if (i == 0 ||
234
0
                     !(eq.operator()(panThisLineVal[i], panThisLineVal[i - 1])))
235
0
            {
236
0
                panThisLineId[i] = NewPolygon(panThisLineVal[i]);
237
0
                if (panThisLineId[i] < 0)
238
0
                    return false;
239
0
            }
240
0
            else
241
0
            {
242
0
                panThisLineId[i] = panThisLineId[i - 1];
243
0
            }
244
0
        }
245
246
0
        return true;
247
0
    }
248
249
    /* -------------------------------------------------------------------- */
250
    /*      Process each pixel comparing to the previous pixel, and to      */
251
    /*      the last line.                                                  */
252
    /* -------------------------------------------------------------------- */
253
0
    for (int i = 0; i < nXSize; i++)
254
0
    {
255
0
        if (panThisLineVal[i] == GP_NODATA_MARKER)
256
0
        {
257
0
            panThisLineId[i] = -1;
258
0
        }
259
0
        else if (i > 0 &&
260
0
                 eq.operator()(panThisLineVal[i], panThisLineVal[i - 1]))
261
0
        {
262
0
            panThisLineId[i] = panThisLineId[i - 1];
263
264
0
            if (eq.operator()(panLastLineVal[i], panThisLineVal[i]) &&
265
0
                (panPolyIdMap[panLastLineId[i]] !=
266
0
                 panPolyIdMap[panThisLineId[i]]))
267
0
            {
268
0
                MergePolygon(panLastLineId[i], panThisLineId[i]);
269
0
            }
270
271
0
            if (nConnectedness == 8 &&
272
0
                eq.operator()(panLastLineVal[i - 1], panThisLineVal[i]) &&
273
0
                (panPolyIdMap[panLastLineId[i - 1]] !=
274
0
                 panPolyIdMap[panThisLineId[i]]))
275
0
            {
276
0
                MergePolygon(panLastLineId[i - 1], panThisLineId[i]);
277
0
            }
278
279
0
            if (nConnectedness == 8 && i < nXSize - 1 &&
280
0
                eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]) &&
281
0
                (panPolyIdMap[panLastLineId[i + 1]] !=
282
0
                 panPolyIdMap[panThisLineId[i]]))
283
0
            {
284
0
                MergePolygon(panLastLineId[i + 1], panThisLineId[i]);
285
0
            }
286
0
        }
287
0
        else if (eq.operator()(panLastLineVal[i], panThisLineVal[i]))
288
0
        {
289
0
            panThisLineId[i] = panLastLineId[i];
290
0
        }
291
0
        else if (i > 0 && nConnectedness == 8 &&
292
0
                 eq.operator()(panLastLineVal[i - 1], panThisLineVal[i]))
293
0
        {
294
0
            panThisLineId[i] = panLastLineId[i - 1];
295
296
0
            if (i < nXSize - 1 &&
297
0
                eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]) &&
298
0
                (panPolyIdMap[panLastLineId[i + 1]] !=
299
0
                 panPolyIdMap[panThisLineId[i]]))
300
0
            {
301
0
                MergePolygon(panLastLineId[i + 1], panThisLineId[i]);
302
0
            }
303
0
        }
304
0
        else if (i < nXSize - 1 && nConnectedness == 8 &&
305
0
                 eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]))
306
0
        {
307
0
            panThisLineId[i] = panLastLineId[i + 1];
308
0
        }
309
0
        else
310
0
        {
311
0
            panThisLineId[i] = NewPolygon(panThisLineVal[i]);
312
0
            if (panThisLineId[i] < 0)
313
0
                return false;
314
0
        }
315
0
    }
316
0
    return true;
317
0
}
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<long, IntEqualityTest>::ProcessLine(long*, long*, int*, int*, int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>::ProcessLine(float*, float*, int*, int*, int)
Unexecuted instantiation: GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>::ProcessLine(double*, double*, int*, int*, int)
318
319
template class GDALRasterPolygonEnumeratorT<std::int64_t, IntEqualityTest>;
320
321
template class GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>;
322
323
template class GDALRasterPolygonEnumeratorT<double, DoubleEqualityTest>;
324
325
/*! @endcond */