Coverage Report

Created: 2025-08-25 06:57

/src/geos/src/coverage/CoverageRing.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2022 Paul Ramsey <pramsey@cleverelephant.ca>
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************/
14
15
#include <geos/coverage/CoverageRing.h>
16
17
#include <geos/algorithm/Orientation.h>
18
#include <geos/geom/Coordinate.h>
19
#include <geos/geom/CoordinateSequence.h>
20
#include <geos/geom/Geometry.h>
21
#include <geos/geom/GeometryFactory.h>
22
#include <geos/geom/LineString.h>
23
#include <geos/geom/LinearRing.h>
24
#include <geos/geom/Polygon.h>
25
#include <geos/geom/util/PolygonExtracter.h>
26
#include <geos/util/IllegalStateException.h>
27
28
using geos::geom::Coordinate;
29
using geos::geom::CoordinateSequence;
30
using geos::geom::Geometry;
31
using geos::geom::GeometryFactory;
32
using geos::geom::LineString;
33
using geos::geom::LinearRing;
34
using geos::geom::Polygon;
35
36
37
namespace geos {     // geos
38
namespace coverage { // geos.coverage
39
40
41
/* public static */
42
bool
43
CoverageRing::isKnown(std::vector<CoverageRing*>& rings)
44
0
{
45
0
    for (auto* ring : rings) {
46
0
        if (! ring->isKnown())
47
0
            return false;
48
0
    }
49
0
    return true;
50
0
}
51
52
53
/* public */
54
CoverageRing::CoverageRing(CoordinateSequence* inPts, bool interiorOnRight)
55
0
    : noding::BasicSegmentString(inPts, nullptr)
56
0
    , m_isInteriorOnRight(interiorOnRight)
57
0
{
58
0
    m_isInvalid.resize(size() - 1, false);
59
0
    m_isMatched.resize(size() - 1, false);
60
0
}
61
62
63
/* public */
64
CoverageRing::CoverageRing(const LinearRing* ring, bool isShell)
65
0
    : CoverageRing(
66
        // This is bad. The ownership rules of SegmentStrings need
67
        // to be carefully considered. Most noders don't even touch
68
        // them so a const CoordinateSequence makes sense. Some add
69
        // things, like the NodedSegmentString, but do so out-of-line.
70
        // Some noders (just ScalingNoder?) completely transform the
71
        // inputs. Could maybe do bulk copying for that case?
72
0
        const_cast<CoordinateSequence*>(ring->getCoordinatesRO()),
73
0
        algorithm::Orientation::isCCW(ring->getCoordinatesRO()) != isShell)
74
0
{}
75
76
/* public */ 
77
geom::Envelope CoverageRing::getEnvelope(std::size_t start, std::size_t end) 
78
0
{
79
0
    geom::Envelope env;
80
0
    for (std::size_t i = start; i < end; i++) {
81
0
        env.expandToInclude(getCoordinate(i));
82
0
    }
83
0
    return env;
84
0
}
85
86
87
/* public */
88
bool
89
CoverageRing::isInteriorOnRight() const
90
0
{
91
0
    return m_isInteriorOnRight;
92
0
}
93
94
95
/* public */
96
void
97
CoverageRing::markInvalid(std::size_t index)
98
0
{
99
0
    m_isInvalid[index] = true;
100
0
}
101
102
103
/* public */
104
void
105
CoverageRing::markMatched(std::size_t index)
106
0
{
107
0
    m_isMatched[index] = true;
108
0
}
109
110
111
/* public */
112
bool
113
CoverageRing::isKnown() const
114
0
{
115
0
    for (size_t i = 0; i < m_isMatched.size(); i++ ) {
116
0
        if (!(m_isMatched[i] && m_isInvalid[i]))
117
0
            return false;
118
0
    }
119
0
    return true;
120
0
}
121
122
/* public */
123
bool
124
CoverageRing::isInvalid(std::size_t i) const
125
0
{
126
0
    return m_isInvalid[i];
127
0
}
128
129
/* public */
130
bool
131
CoverageRing::isInvalid() const
132
0
{
133
0
    for (bool b: m_isInvalid) {
134
0
        if (!b)
135
0
            return false;
136
0
    }
137
0
    return true;
138
0
}
139
140
141
/* public */
142
bool
143
CoverageRing::hasInvalid() const
144
0
{
145
0
    for (bool b: m_isInvalid) {
146
0
        if (b)
147
0
            return true;
148
0
    }
149
0
    return false;
150
0
}
151
152
153
/* public */
154
bool
155
CoverageRing::isKnown(std::size_t i) const
156
0
{
157
0
    return m_isMatched[i] || m_isInvalid[i];
158
0
}
159
160
161
/* public */
162
const Coordinate&
163
CoverageRing::findVertexPrev(std::size_t index, const Coordinate& pt) const
164
0
{
165
0
    std::size_t iPrev = index;
166
0
    const Coordinate* cPrev = &getCoordinate(iPrev);
167
0
    while (pt.equals2D(*cPrev)) {
168
0
        iPrev = prev(iPrev);
169
0
        cPrev = &getCoordinate(iPrev);
170
0
    }
171
0
    return *cPrev;
172
0
}
173
174
175
/* public */
176
const Coordinate&
177
CoverageRing::findVertexNext(std::size_t index, const Coordinate& pt) const
178
0
{
179
    //-- safe, since index is always the start of a segment
180
0
    std::size_t iNext = index + 1;
181
0
    const Coordinate* cNext = &getCoordinate(iNext);
182
0
    while (pt.equals2D(*cNext)) {
183
0
        iNext = next(iNext);
184
0
        cNext = &getCoordinate(iNext);
185
0
    }
186
0
    return *cNext;
187
0
}
188
189
190
/* public */
191
std::size_t
192
CoverageRing::prev(std::size_t index) const
193
0
{
194
0
    if (index == 0)
195
0
        return size() - 2;
196
0
    return index - 1;
197
0
}
198
199
200
/* public */
201
std::size_t
202
CoverageRing::next(std::size_t index) const
203
0
{
204
0
    if (index < size() - 2)
205
0
        return index + 1;
206
0
    return 0;
207
0
}
208
209
210
/* public */
211
void
212
CoverageRing::createInvalidLines(
213
    const GeometryFactory* geomFactory,
214
    std::vector<std::unique_ptr<LineString>>& lines)
215
0
{
216
    //-- empty case
217
0
    if (! hasInvalid()) {
218
0
        return;
219
0
    }
220
    //-- entire ring case
221
0
    if (isInvalid()) {
222
0
        std::unique_ptr<LineString> line = createLine(0, size() - 1, geomFactory);
223
0
        lines.push_back(std::move(line));
224
0
        return;
225
0
    }
226
227
    //-- find first end after index 0, to allow wrap-around
228
0
    std::size_t startIndex = findInvalidStart(0);
229
0
    std::size_t firstEndIndex = findInvalidEnd(startIndex);
230
0
    std::size_t endIndex = firstEndIndex;
231
0
    while (true) {
232
0
        startIndex = findInvalidStart(endIndex);
233
0
        endIndex = findInvalidEnd(startIndex);
234
0
        std::unique_ptr<LineString> line = createLine(startIndex, endIndex, geomFactory);
235
0
        lines.push_back(std::move(line));
236
0
        if (endIndex == firstEndIndex)
237
0
            break;
238
0
    }
239
0
}
240
241
242
/* private */
243
std::size_t
244
CoverageRing::findInvalidStart(std::size_t index)
245
0
{
246
0
    while (! isInvalid(index)) {
247
0
        index = nextMarkIndex(index);
248
0
    }
249
0
    return index;
250
0
}
251
252
253
/* private */
254
std::size_t
255
CoverageRing::findInvalidEnd(std::size_t index)
256
0
{
257
0
    index = nextMarkIndex(index);
258
0
    while (isInvalid(index)) {
259
0
        index = nextMarkIndex(index);
260
0
    }
261
0
    return index;
262
0
}
263
264
265
/* private */
266
std::size_t
267
CoverageRing::nextMarkIndex(std::size_t index)
268
0
{
269
0
    if (index >= m_isInvalid.size() - 1) {
270
0
        return 0;
271
0
    }
272
0
    return index + 1;
273
0
}
274
275
276
/* private */
277
std::unique_ptr<LineString>
278
CoverageRing::createLine(
279
    std::size_t startIndex,
280
    std::size_t endIndex,
281
    const GeometryFactory* geomFactory)
282
0
{
283
0
    std::unique_ptr<CoordinateSequence> linePts = endIndex < startIndex
284
0
        ? extractSectionWrap(startIndex, endIndex)
285
0
        : extractSection(startIndex, endIndex);
286
0
    return geomFactory->createLineString(std::move(linePts));
287
0
}
288
289
290
/* private */
291
std::unique_ptr<CoordinateSequence>
292
CoverageRing::extractSection(std::size_t startIndex, std::size_t endIndex)
293
0
{
294
    // std::size_t sz = endIndex - startIndex + 1;
295
0
    std::unique_ptr<CoordinateSequence> linePts(new CoordinateSequence());
296
0
    for (std::size_t i = startIndex; i <= endIndex; i++) {
297
0
        linePts->add(getCoordinate(i));
298
0
    }
299
300
0
    return linePts;
301
0
}
302
303
304
/* private */
305
std::unique_ptr<CoordinateSequence>
306
CoverageRing::extractSectionWrap(std::size_t startIndex, std::size_t endIndex)
307
0
{
308
0
    std::size_t sz = endIndex + (size() - startIndex);
309
0
    std::unique_ptr<CoordinateSequence> linePts(new CoordinateSequence);
310
0
    std::size_t index = startIndex;
311
0
    for (std::size_t i = 0; i < sz; i++) {
312
0
        linePts->add(getCoordinate(index));
313
0
        index = nextMarkIndex(index);
314
0
    }
315
316
0
    return linePts;
317
0
}
318
319
320
} // namespace geos.coverage
321
} // namespace geos
322
323