Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/marching_squares/segment_merger.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Marching square algorithm
4
 * Purpose:  Core algorithm implementation for contour line generation.
5
 * Author:   Oslandia <infos at oslandia dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
#ifndef MARCHING_SQUARES_SEGMENT_MERGER_H
13
#define MARCHING_SQUARES_SEGMENT_MERGER_H
14
15
#include "cpl_error.h"
16
#include "point.h"
17
18
#include <algorithm>
19
#include <cassert>
20
#include <list>
21
#include <map>
22
#include <vector>
23
24
#include <iostream>
25
26
namespace marching_squares
27
{
28
29
// SegmentMerger: join segments into linestrings and possibly into rings of
30
// polygons
31
template <typename LineWriter, typename LevelGenerator> struct SegmentMerger
32
{
33
    struct LineStringEx
34
    {
35
        LineString ls = LineString();
36
        bool isMerged = false;
37
    };
38
39
    // a collection of unmerged linestrings
40
    typedef std::list<LineStringEx> Lines;
41
42
    SegmentMerger(LineWriter &lineWriter, const LevelGenerator &levelGenerator,
43
                  bool polygonize_)
44
0
        : polygonize(polygonize_), lineWriter_(lineWriter), lines_(),
45
0
          endpointIndex_(), levelGenerator_(levelGenerator), m_anSkipLevels()
46
0
    {
47
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::SegmentMerger(GDALRingAppender&, marching_squares::IntervalLevelRangeIterator const&, bool)
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::SegmentMerger(marching_squares::PolygonRingAppender<PolygonContourWriter>&, marching_squares::FixedLevelRangeIterator const&, bool)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::SegmentMerger(GDALRingAppender&, marching_squares::FixedLevelRangeIterator const&, bool)
48
49
    ~SegmentMerger()
50
0
    {
51
0
        if (polygonize)
52
0
        {
53
0
            for (auto it = lines_.begin(); it != lines_.end(); ++it)
54
0
            {
55
0
                if (!it->second.empty())
56
0
                    debug("remaining unclosed contour");
57
0
            }
58
0
        }
59
        // write all remaining (non-closed) lines
60
0
        for (auto it = lines_.begin(); it != lines_.end(); ++it)
61
0
        {
62
0
            const int levelIdx = it->first;
63
64
            // Skip levels that should be skipped
65
0
            if (std::find(m_anSkipLevels.begin(), m_anSkipLevels.end(),
66
0
                          levelIdx) != m_anSkipLevels.end())
67
0
            {
68
0
                continue;
69
0
            }
70
0
            while (it->second.begin() != it->second.end())
71
0
            {
72
0
                lineWriter_.addLine(levelGenerator_.level(levelIdx),
73
0
                                    it->second.begin()->ls, /* closed */ false);
74
0
                it->second.pop_front();
75
0
            }
76
0
        }
77
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::~SegmentMerger()
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::~SegmentMerger()
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::~SegmentMerger()
78
79
    void addSegment(int levelIdx, const Point &start, const Point &end)
80
0
    {
81
0
        addSegment_(levelIdx, start, end);
82
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&)
83
84
    void addBorderSegment(int levelIdx, const Point &start, const Point &end)
85
0
    {
86
0
        addSegment_(levelIdx, start, end);
87
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&)
88
89
    void beginningOfLine()
90
0
    {
91
0
        if (polygonize)
92
0
            return;
93
94
        // mark lines as non merged
95
0
        for (auto &l : lines_)
96
0
        {
97
0
            for (auto &ls : l.second)
98
0
            {
99
0
                ls.isMerged = false;
100
0
            }
101
0
        }
102
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::beginningOfLine()
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::beginningOfLine()
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::beginningOfLine()
103
104
    void endOfLine()
105
0
    {
106
0
        if (polygonize)
107
0
            return;
108
109
        // At the end of the line, we know that if no segment has been merged to
110
        // an existing line, it means there won't be anything more in the
111
        // future, we can then emit the line (this both speeds up and saves
112
        // memory)
113
114
0
        for (auto &l : lines_)
115
0
        {
116
0
            const int levelIdx = l.first;
117
0
            auto it = l.second.begin();
118
0
            while (it != l.second.end())
119
0
            {
120
0
                if (!it->isMerged)
121
0
                {
122
                    // Note that emitLine_ erases `it` and returns an iterator
123
                    // advanced to the next element.
124
0
                    it = emitLine_(levelIdx, it, /* closed */ false);
125
0
                }
126
0
                else
127
0
                {
128
0
                    ++it;
129
0
                }
130
0
            }
131
0
        }
132
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::endOfLine()
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::endOfLine()
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::endOfLine()
133
134
    // non copyable
135
    SegmentMerger(const SegmentMerger<LineWriter, LevelGenerator> &) = delete;
136
    SegmentMerger<LineWriter, LevelGenerator> &
137
    operator=(const SegmentMerger<LineWriter, LevelGenerator> &) = delete;
138
139
    /**
140
     * @brief setSkipLevels sets the levels that should be skipped
141
     *        when polygonize option is set.
142
     * @param anSkipLevels integer 0-based levels to skip.
143
     */
144
    void setSkipLevels(const std::vector<int> &anSkipLevels)
145
0
    {
146
        // Warn if polygonize is not set
147
0
        if (!polygonize)
148
0
        {
149
0
            CPLError(
150
0
                CE_Warning, CPLE_NotSupported,
151
0
                "setSkipLevels is ignored when polygonize option is not set");
152
0
        }
153
0
        m_anSkipLevels = anSkipLevels;
154
0
    }
155
156
    const bool polygonize;
157
158
  private:
159
    LineWriter &lineWriter_;
160
    // lines of each level
161
    std::map<int, Lines> lines_;
162
163
    // Index of the open lines of each level, keyed by their two endpoints.
164
    // Marching squares emits segments whose endpoints have degree two, so a
165
    // point can only ever be shared by two segment ends; a lookup here
166
    // replaces a linear scan over all open lines, which is quadratic on
167
    // rasters that keep many lines open at once (e.g. many long parallel
168
    // contours crossing each scanline).
169
    struct PointCompare
170
    {
171
        bool operator()(const Point &a, const Point &b) const
172
0
        {
173
0
            return a.x < b.x || (a.x == b.x && a.y < b.y);
174
0
        }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const
175
    };
176
177
    // A marching squares point has degree two, so at most two line ends can
178
    // sit on it (the two ends of the same line, when it has closed): a fixed
179
    // two-slot entry avoids a heap allocation per endpoint.
180
    struct EndpointEntry
181
    {
182
        typename Lines::iterator its[2];
183
        int n = 0;
184
    };
185
186
    typedef std::map<Point, EndpointEntry, PointCompare> EndpointIndex;
187
    std::map<int, EndpointIndex> endpointIndex_;
188
189
    const LevelGenerator &levelGenerator_;
190
191
    // Store 0-indexed levels to skip when polygonize option is set
192
    std::vector<int> m_anSkipLevels;
193
194
    void registerEndpoint_(EndpointIndex &idx, typename Lines::iterator it,
195
                           const Point &p)
196
0
    {
197
0
        EndpointEntry &e = idx[p];
198
0
        assert(e.n < 2);
199
0
        e.its[e.n++] = it;
200
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
201
202
    void unregisterEndpoint_(EndpointIndex &idx, typename Lines::iterator it,
203
                             const Point &p)
204
0
    {
205
0
        auto f = idx.find(p);
206
0
        assert(f != idx.end());
207
0
        EndpointEntry &e = f->second;
208
0
        if (e.its[0] == it)
209
0
            e.its[0] = e.its[1];
210
0
        else
211
0
            assert(e.n == 2 && e.its[1] == it);
212
0
        e.n--;
213
0
        if (e.n == 0)
214
0
            idx.erase(f);
215
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&)
216
217
    void addSegment_(int levelIdx, const Point &start, const Point &end)
218
0
    {
219
220
0
        Lines &lines = lines_[levelIdx];
221
222
0
        if (start == end)
223
0
        {
224
0
            debug("degenerate segment (%f %f)", start.x, start.y);
225
0
            return;
226
0
        }
227
228
0
        auto idxIt = endpointIndex_.find(levelIdx);
229
0
        if (idxIt == endpointIndex_.end())
230
0
        {
231
0
            addSegmentLinear_(levelIdx, lines, start, end);
232
            // The linear scans above are quadratic when many lines stay open
233
            // at once (e.g. long parallel contours crossing each scanline):
234
            // past this size, switch the level to the endpoint index. Below
235
            // it, the scans are cheaper than maintaining the index.
236
0
            constexpr std::size_t INDEX_THRESHOLD = 100;
237
0
            if (lines.size() > INDEX_THRESHOLD)
238
0
            {
239
0
                EndpointIndex &idx = endpointIndex_[levelIdx];
240
0
                for (auto it = lines.begin(); it != lines.end(); ++it)
241
0
                {
242
0
                    registerEndpoint_(idx, it, it->ls.front());
243
0
                    registerEndpoint_(idx, it, it->ls.back());
244
0
                }
245
0
            }
246
0
        }
247
0
        else
248
0
        {
249
0
            addSegmentIndexed_(levelIdx, lines, idxIt->second, start, end);
250
0
        }
251
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&)
252
253
    // Merge the segment by scanning the open lines: cheap while there are
254
    // few of them, quadratic when there are many.
255
    void addSegmentLinear_(int levelIdx, Lines &lines, const Point &start,
256
                           const Point &end)
257
0
    {
258
        // attempt to merge segment with existing line
259
0
        auto it = lines.begin();
260
0
        for (; it != lines.end(); ++it)
261
0
        {
262
0
            if (it->ls.back() == end)
263
0
            {
264
0
                it->ls.push_back(start);
265
0
                it->isMerged = true;
266
0
                break;
267
0
            }
268
0
            if (it->ls.front() == end)
269
0
            {
270
0
                it->ls.push_front(start);
271
0
                it->isMerged = true;
272
0
                break;
273
0
            }
274
0
            if (it->ls.back() == start)
275
0
            {
276
0
                it->ls.push_back(end);
277
0
                it->isMerged = true;
278
0
                break;
279
0
            }
280
0
            if (it->ls.front() == start)
281
0
            {
282
0
                it->ls.push_front(end);
283
0
                it->isMerged = true;
284
0
                break;
285
0
            }
286
0
        }
287
288
0
        if (it == lines.end())
289
0
        {
290
            // new line
291
0
            lines.push_back(LineStringEx());
292
0
            lines.back().ls.push_back(start);
293
0
            lines.back().ls.push_back(end);
294
0
            lines.back().isMerged = true;
295
0
        }
296
0
        else if (polygonize && (it->ls.front() == it->ls.back()))
297
0
        {
298
            // ring closed
299
0
            emitLine_(levelIdx, it, /* closed */ true);
300
0
            return;
301
0
        }
302
0
        else
303
0
        {
304
            // try to perform linemerge with another line
305
            // since we got out of the previous loop on the first match
306
            // there is no need to test previous elements
307
            // also: a segment merges at most two lines, no need to stall here
308
            // ;)
309
0
            auto other = it;
310
0
            ++other;
311
0
            for (; other != lines.end(); ++other)
312
0
            {
313
0
                if (it->ls.back() == other->ls.front())
314
0
                {
315
0
                    it->ls.pop_back();
316
0
                    it->ls.splice(it->ls.end(), other->ls);
317
0
                    it->isMerged = true;
318
0
                    lines.erase(other);
319
                    // if that makes a closed ring, returns it
320
0
                    if (it->ls.front() == it->ls.back())
321
0
                        emitLine_(levelIdx, it, /* closed */ true);
322
0
                    break;
323
0
                }
324
0
                else if (other->ls.back() == it->ls.front())
325
0
                {
326
0
                    it->ls.pop_front();
327
0
                    other->ls.splice(other->ls.end(), it->ls);
328
0
                    other->isMerged = true;
329
0
                    lines.erase(it);
330
                    // if that makes a closed ring, returns it
331
0
                    if (other->ls.front() == other->ls.back())
332
0
                        emitLine_(levelIdx, other, /* closed */ true);
333
0
                    break;
334
0
                }
335
                // two lists must be merged but one is in the opposite direction
336
0
                else if (it->ls.back() == other->ls.back())
337
0
                {
338
0
                    it->ls.pop_back();
339
0
                    for (auto rit = other->ls.rbegin(); rit != other->ls.rend();
340
0
                         ++rit)
341
0
                    {
342
0
                        it->ls.push_back(*rit);
343
0
                    }
344
0
                    it->isMerged = true;
345
0
                    lines.erase(other);
346
                    // if that makes a closed ring, returns it
347
0
                    if (it->ls.front() == it->ls.back())
348
0
                        emitLine_(levelIdx, it, /* closed */ true);
349
0
                    break;
350
0
                }
351
0
                else if (it->ls.front() == other->ls.front())
352
0
                {
353
0
                    it->ls.pop_front();
354
0
                    for (auto rit = other->ls.begin(); rit != other->ls.end();
355
0
                         ++rit)
356
0
                    {
357
0
                        it->ls.push_front(*rit);
358
0
                    }
359
0
                    it->isMerged = true;
360
0
                    lines.erase(other);
361
                    // if that makes a closed ring, returns it
362
0
                    if (it->ls.front() == it->ls.back())
363
0
                        emitLine_(levelIdx, it, /* closed */ true);
364
0
                    break;
365
0
                }
366
0
            }
367
0
        }
368
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&)
369
370
    // Merge the segment through the endpoint index. Same merging rules as
371
    // the linear scan (a marching squares point has degree two, so at most
372
    // one line can match each of the segment's endpoints), but O(log n)
373
    // lookups instead of O(n) scans.
374
    void addSegmentIndexed_(int levelIdx, Lines &lines, EndpointIndex &idx,
375
                            const Point &start, const Point &end)
376
0
    {
377
        // attempt to merge the segment with an existing line whose endpoint
378
        // matches one of the segment's endpoints
379
0
        auto findLine = [&](const Point &p)
380
0
        {
381
0
            auto f = idx.find(p);
382
0
            return f == idx.end() ? lines.end() : f->second.its[0];
383
0
        };
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) const
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) const
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) const
384
385
0
        Point matched = end;
386
0
        Point added = start;
387
0
        typename Lines::iterator it = findLine(end);
388
0
        if (it == lines.end())
389
0
        {
390
0
            matched = start;
391
0
            added = end;
392
0
            it = findLine(start);
393
0
        }
394
395
0
        if (it == lines.end())
396
0
        {
397
            // new line
398
0
            lines.push_back(LineStringEx());
399
0
            it = std::prev(lines.end());
400
0
            it->ls.push_back(start);
401
0
            it->ls.push_back(end);
402
0
            it->isMerged = true;
403
0
            registerEndpoint_(idx, it, start);
404
0
            registerEndpoint_(idx, it, end);
405
0
            return;
406
0
        }
407
408
        // extend the matched line with the segment's other endpoint
409
0
        unregisterEndpoint_(idx, it, matched);
410
0
        if (it->ls.back() == matched)
411
0
            it->ls.push_back(added);
412
0
        else
413
0
            it->ls.push_front(added);
414
0
        it->isMerged = true;
415
0
        registerEndpoint_(idx, it, added);
416
417
        // The extension may close a ring, or bring the line's new endpoint
418
        // onto another line's endpoint; merging two lines moves the free
419
        // endpoint again, so iterate until neither applies.
420
0
        for (;;)
421
0
        {
422
0
            if (polygonize && it->ls.front() == it->ls.back())
423
0
            {
424
                // ring closed
425
0
                emitLine_(levelIdx, it, /* closed */ true);
426
0
                return;
427
0
            }
428
            // is there another line ending at `added`?
429
0
            auto f = idx.find(added);
430
0
            assert(f != idx.end());
431
0
            typename Lines::iterator other = lines.end();
432
0
            for (int i = 0; i < f->second.n; i++)
433
0
            {
434
0
                if (f->second.its[i] != it)
435
0
                {
436
0
                    other = f->second.its[i];
437
0
                    break;
438
0
                }
439
0
            }
440
0
            if (other == lines.end())
441
0
                return;
442
443
            // merge `other` into `it` at the shared point `added`
444
0
            unregisterEndpoint_(idx, it, added);
445
0
            unregisterEndpoint_(idx, other, added);
446
0
            const Point otherEnd = other->ls.front() == added
447
0
                                       ? other->ls.back()
448
0
                                       : other->ls.front();
449
0
            unregisterEndpoint_(idx, other, otherEnd);
450
0
            if (it->ls.back() == added)
451
0
            {
452
0
                it->ls.pop_back();
453
0
                if (other->ls.front() == added)
454
0
                {
455
0
                    it->ls.splice(it->ls.end(), other->ls);
456
0
                }
457
0
                else
458
0
                {
459
                    // opposite direction: append reversed
460
0
                    for (auto rit = other->ls.rbegin(); rit != other->ls.rend();
461
0
                         ++rit)
462
0
                    {
463
0
                        it->ls.push_back(*rit);
464
0
                    }
465
0
                }
466
0
            }
467
0
            else
468
0
            {
469
0
                it->ls.pop_front();
470
0
                if (other->ls.back() == added)
471
0
                {
472
0
                    it->ls.splice(it->ls.begin(), other->ls);
473
0
                }
474
0
                else
475
0
                {
476
                    // opposite direction: prepend reversed
477
0
                    for (auto rit = other->ls.begin(); rit != other->ls.end();
478
0
                         ++rit)
479
0
                    {
480
0
                        it->ls.push_front(*rit);
481
0
                    }
482
0
                }
483
0
            }
484
0
            lines.erase(other);
485
0
            registerEndpoint_(idx, it, otherEnd);
486
0
            added = otherEnd;
487
0
        }
488
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)
489
490
    typename Lines::iterator emitLine_(int levelIdx,
491
                                       typename Lines::iterator it, bool closed)
492
0
    {
493
494
0
        Lines &lines = lines_[levelIdx];
495
0
        if (lines.empty())
496
0
            lines_.erase(levelIdx);
497
498
0
        auto idxIt = endpointIndex_.find(levelIdx);
499
0
        if (idxIt != endpointIndex_.end())
500
0
        {
501
0
            unregisterEndpoint_(idxIt->second, it, it->ls.front());
502
0
            unregisterEndpoint_(idxIt->second, it, it->ls.back());
503
            // An emptied index means no line is open at this level anymore:
504
            // drop it so the level returns to the linear path until it grows
505
            // past the threshold again.
506
0
            if (idxIt->second.empty())
507
0
                endpointIndex_.erase(idxIt);
508
0
        }
509
510
        // consume "it" and remove it from the list
511
        // but clear the line if the level should be skipped
512
0
        if (std::find(m_anSkipLevels.begin(), m_anSkipLevels.end(), levelIdx) !=
513
0
            m_anSkipLevels.end())
514
0
        {
515
0
            it->ls.clear();
516
0
        }
517
0
        lineWriter_.addLine(levelGenerator_.level(levelIdx), it->ls, closed);
518
0
        return lines.erase(it);
519
0
    }
Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, bool)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, bool)
Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, bool)
520
};
521
522
}  // namespace marching_squares
523
#endif