Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/geom/SimpleCurve.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
7
 * Copyright (C) 2005 2006 Refractions Research Inc.
8
 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
9
 * Copyright (C) 2024 ISciences, LLC
10
 *
11
 * This is free software; you can redistribute and/or modify it under
12
 * the terms of the GNU Lesser General Public Licence as published
13
 * by the Free Software Foundation.
14
 * See the COPYING file for more information.
15
 *
16
 **********************************************************************/
17
18
#include <geos/geom/SimpleCurve.h>
19
20
#include <geos/algorithm/CircularArcs.h>
21
#include <geos/algorithm/Orientation.h>
22
#include <geos/geom/CoordinateFilter.h>
23
#include <geos/geom/GeometryFactory.h>
24
#include <geos/geom/GeometryFilter.h>
25
#include <geos/operation/BoundaryOp.h>
26
#include <geos/geom/CoordinateSequence.h>
27
#include <geos/geom/CoordinateSequenceFilter.h>
28
#include <geos/util.h>
29
30
31
namespace geos {
32
namespace geom {
33
34
SimpleCurve::SimpleCurve(const SimpleCurve& other)
35
7.88M
    : Curve(other),
36
7.88M
      points(other.points->clone()),
37
7.88M
      envelope(other.envelope)
38
7.88M
{
39
7.88M
}
40
41
SimpleCurve::SimpleCurve(const std::shared_ptr<const CoordinateSequence>& newCoords,
42
                         bool isLinear,
43
                         const GeometryFactory& factory)
44
218k
    : Curve(factory),
45
218k
      points(newCoords),
46
218k
      envelope(computeEnvelopeInternal(isLinear))
47
218k
{
48
218k
}
49
50
SimpleCurve::SimpleCurve(std::unique_ptr<CoordinateSequence>&& newCoords,
51
                         bool isLinear,
52
                         const GeometryFactory& factory)
53
5.01M
    : Curve(factory),
54
5.01M
      points(newCoords ? std::move(newCoords) : std::make_shared<CoordinateSequence>()),
55
5.01M
      envelope(computeEnvelopeInternal(isLinear))
56
5.01M
{
57
5.01M
}
58
59
void
60
SimpleCurve::apply_ro(CoordinateFilter* filter) const
61
0
{
62
0
    assert(points.get());
63
0
    points->apply_ro(filter);
64
0
}
65
66
void
67
SimpleCurve::apply_ro(CoordinateSequenceFilter& filter) const
68
873k
{
69
873k
    std::size_t npts = points->size();
70
873k
    if (!npts) {
71
2.13k
        return;
72
2.13k
    }
73
2.06M
    for (std::size_t i = 0; i < npts; ++i) {
74
1.78M
        filter.filter_ro(*points, i);
75
1.78M
        if (filter.isDone()) {
76
595k
            break;
77
595k
        }
78
1.78M
    }
79
871k
}
80
81
void
82
SimpleCurve::apply_rw(const CoordinateFilter* filter)
83
232k
{
84
232k
    assert(points.get());
85
232k
    if (points.use_count() > 1) {
86
0
        points = points->clone();
87
0
    }
88
232k
    const_cast<CoordinateSequence*>(points.get())->apply_rw(filter);
89
232k
}
90
91
void
92
SimpleCurve::apply_rw(CoordinateSequenceFilter& filter)
93
0
{
94
0
    if (points.use_count() > 1) {
95
0
        points = points->clone();
96
0
    }
97
0
    std::size_t npts = points->size();
98
0
    if (!npts) {
99
0
        return;
100
0
    }
101
0
    for (std::size_t i = 0; i < npts; ++i) {
102
0
        filter.filter_rw(const_cast<CoordinateSequence&>(*points), i);
103
0
        if (filter.isDone()) {
104
0
            break;
105
0
        }
106
0
    }
107
0
    if (filter.isGeometryChanged()) {
108
0
        geometryChanged();
109
0
    }
110
0
}
111
112
int
113
SimpleCurve::compareToSameClass(const Geometry* ls) const
114
0
{
115
0
    const SimpleCurve* line = detail::down_cast<const SimpleCurve*>(ls);
116
117
    // MD - optimized implementation
118
0
    std::size_t mynpts = points->getSize();
119
0
    std::size_t othnpts = line->points->getSize();
120
0
    if (mynpts > othnpts) {
121
0
        return 1;
122
0
    }
123
0
    if (mynpts < othnpts) {
124
0
        return -1;
125
0
    }
126
0
    for (std::size_t i = 0; i < mynpts; i++) {
127
0
        int cmp = points->getAt<CoordinateXY>(i).compareTo(line->points->getAt<CoordinateXY>(i));
128
0
        if (cmp) {
129
0
            return cmp;
130
0
        }
131
0
    }
132
0
    return 0;
133
0
}
134
135
Envelope
136
SimpleCurve::computeEnvelopeInternal(bool isLinear) const
137
5.23M
{
138
5.23M
    if (isEmpty()) {
139
118k
        return Envelope();
140
118k
    }
141
142
5.11M
    if (isLinear) {
143
5.11M
        return points->getEnvelope();
144
5.11M
    }
145
344
    else {
146
344
        Envelope e;
147
33.6k
        for (std::size_t i = 2; i < points->size(); i += 2) {
148
33.3k
            algorithm::CircularArcs::expandEnvelope(e,
149
33.3k
                                                    points->getAt<CoordinateXY>(i-2),
150
33.3k
                                                    points->getAt<CoordinateXY>(i-1),
151
33.3k
                                                    points->getAt<CoordinateXY>(i));
152
33.3k
        }
153
344
        return e;
154
344
    }
155
5.11M
}
156
157
bool
158
SimpleCurve::equalsExact(const Geometry* other, double tolerance) const
159
0
{
160
0
    if (!isEquivalentClass(other)) {
161
0
        return false;
162
0
    }
163
164
0
    const SimpleCurve* otherCurve = detail::down_cast<const SimpleCurve*>(other);
165
0
    std::size_t npts = points->getSize();
166
0
    if (npts != otherCurve->points->getSize()) {
167
0
        return false;
168
0
    }
169
0
    for (std::size_t i = 0; i < npts; ++i) {
170
0
        if (!equal(points->getAt<CoordinateXY>(i), otherCurve->points->getAt<CoordinateXY>(i), tolerance)) {
171
0
            return false;
172
0
        }
173
0
    }
174
0
    return true;
175
0
}
176
177
bool
178
SimpleCurve::equalsIdentical(const Geometry* other_g) const
179
0
{
180
0
    if (!isEquivalentClass(other_g)) {
181
0
        return false;
182
0
    }
183
184
0
    const auto& other = static_cast<const SimpleCurve&>(*other_g);
185
186
0
    if (envelope != other.envelope) {
187
0
        return false;
188
0
    }
189
190
0
    return getCoordinatesRO()->equalsIdentical(*other.getCoordinatesRO());
191
0
}
192
193
std::unique_ptr<Geometry>
194
SimpleCurve::getBoundary() const
195
0
{
196
0
    operation::BoundaryOp bop(*this);
197
0
    return bop.getBoundary();
198
0
}
199
200
const CoordinateXY*
201
SimpleCurve::getCoordinate() const
202
0
{
203
0
    if (isEmpty()) {
204
0
        return nullptr;
205
0
    }
206
0
    return &(points->getAt<CoordinateXY>(0));
207
0
}
208
209
uint8_t
210
SimpleCurve::getCoordinateDimension() const
211
1.64M
{
212
1.64M
    return (uint8_t) points->getDimension();
213
1.64M
}
214
215
const Coordinate&
216
SimpleCurve::getCoordinateN(std::size_t n) const
217
164
{
218
164
    assert(points.get());
219
164
    return points->getAt(n);
220
164
}
221
222
std::unique_ptr<CoordinateSequence>
223
SimpleCurve::getCoordinates() const
224
0
{
225
0
    assert(points.get());
226
0
    return points->clone();
227
0
}
228
229
const CoordinateSequence*
230
SimpleCurve::getCoordinatesRO() const
231
11.6M
{
232
11.6M
    assert(nullptr != points.get());
233
11.6M
    return points.get();
234
11.6M
}
235
236
const std::shared_ptr<const CoordinateSequence>&
237
SimpleCurve::getSharedCoordinates() const
238
0
{
239
0
    assert(nullptr != points.get());
240
0
    return points;
241
0
}
242
243
const SimpleCurve*
244
SimpleCurve::getCurveN(std::size_t) const
245
653
{
246
653
    return this;
247
653
}
248
249
std::unique_ptr<Point>
250
SimpleCurve::getEndPoint() const
251
0
{
252
0
    if (isEmpty()) {
253
0
        return nullptr;
254
0
    }
255
0
    return getPointN(getNumPoints() - 1);
256
0
}
257
258
std::size_t
259
SimpleCurve::getNumCurves() const
260
1.30k
{
261
1.30k
    return 1;
262
1.30k
}
263
264
std::size_t
265
SimpleCurve::getNumPoints() const
266
82
{
267
82
    assert(points.get());
268
82
    return points->getSize();
269
82
}
270
271
std::unique_ptr<Point>
272
SimpleCurve::getPointN(std::size_t n) const
273
0
{
274
0
    assert(getFactory());
275
0
    assert(points.get());
276
277
0
    return points->applyAt(n, [this](const auto& c) {
278
0
        return getFactory()->createPoint(c);
279
0
    });
Unexecuted instantiation: SimpleCurve.cpp:auto geos::geom::SimpleCurve::getPointN(unsigned long) const::$_0::operator()<geos::geom::Coordinate>(geos::geom::Coordinate const&) const
Unexecuted instantiation: SimpleCurve.cpp:auto geos::geom::SimpleCurve::getPointN(unsigned long) const::$_0::operator()<geos::geom::CoordinateXYM>(geos::geom::CoordinateXYM const&) const
Unexecuted instantiation: SimpleCurve.cpp:auto geos::geom::SimpleCurve::getPointN(unsigned long) const::$_0::operator()<geos::geom::CoordinateXYZM>(geos::geom::CoordinateXYZM const&) const
Unexecuted instantiation: SimpleCurve.cpp:auto geos::geom::SimpleCurve::getPointN(unsigned long) const::$_0::operator()<geos::geom::CoordinateXY>(geos::geom::CoordinateXY const&) const
280
0
}
281
282
std::unique_ptr<Point>
283
SimpleCurve::getStartPoint() const
284
0
{
285
0
    if (isEmpty()) {
286
0
        return nullptr;
287
0
    }
288
0
    return getPointN(0);
289
0
}
290
291
bool
292
SimpleCurve::hasM() const
293
6.99M
{
294
6.99M
    return points->hasM();
295
6.99M
}
296
297
bool
298
SimpleCurve::hasZ() const
299
7.34M
{
300
7.34M
    return points->hasZ();
301
7.34M
}
302
303
bool
304
SimpleCurve::isClosed() const
305
2.75M
{
306
2.75M
    if (isEmpty()) {
307
15
        return false;
308
15
    }
309
310
2.75M
    return points->front<CoordinateXY>().equals2D(points->back<CoordinateXY>());
311
2.75M
}
312
313
bool
314
SimpleCurve::isCoordinate(CoordinateXY& pt) const
315
0
{
316
0
    assert(points.get());
317
0
    std::size_t npts = points->getSize();
318
0
    for (std::size_t i = 0; i < npts; i++) {
319
0
        if (points->getAt<CoordinateXY>(i) == pt) {
320
0
            return true;
321
0
        }
322
0
    }
323
0
    return false;
324
0
}
325
326
bool
327
SimpleCurve::isEmpty() const
328
31.4M
{
329
31.4M
    assert(points.get());
330
31.4M
    return points->isEmpty();
331
31.4M
}
332
333
/*public*/
334
void
335
SimpleCurve::normalize()
336
0
{
337
0
    util::ensureNoCurvedComponents(*this);
338
339
0
    if (isEmpty()) return;
340
0
    assert(points.get());
341
0
    if (isClosed()) {
342
0
        normalizeClosed();
343
0
        return;
344
0
    }
345
0
    std::size_t npts = points->getSize();
346
0
    std::size_t n = npts / 2;
347
0
    for (std::size_t i = 0; i < n; i++) {
348
0
        std::size_t j = npts - 1 - i;
349
0
        if (!(points->getAt<CoordinateXY>(i) == points->getAt<CoordinateXY>(j))) {
350
0
            if (points->getAt<CoordinateXY>(i).compareTo(points->getAt<CoordinateXY>(j)) > 0) {
351
0
                if (points.use_count() > 1) {
352
0
                    points = points->clone();
353
0
                }
354
0
                const_cast<CoordinateSequence*>(points.get())->reverse();
355
0
            }
356
0
            return;
357
0
        }
358
0
    }
359
0
}
360
361
/*private*/
362
void
363
SimpleCurve::normalizeClosed()
364
0
{
365
0
    if (isEmpty()) {
366
0
        return;
367
0
    }
368
369
0
    const auto& ringCoords = getCoordinatesRO();
370
371
0
    auto coords = detail::make_unique<CoordinateSequence>(0u, ringCoords->hasZ(), ringCoords->hasM());
372
0
    coords->reserve(ringCoords->size());
373
    // exclude last point (repeated)
374
0
    coords->add(*ringCoords, 0, ringCoords->size() - 2);
375
376
0
    const CoordinateXY* minCoordinate = coords->minCoordinate();
377
378
0
    CoordinateSequence::scroll(coords.get(), minCoordinate);
379
0
    coords->closeRing(true);
380
381
0
    if (coords->size() >= 4 && algorithm::Orientation::isCCW(coords.get())) {
382
0
        coords->reverse();
383
0
    }
384
385
0
    points = std::move(coords);
386
0
}
387
388
} // namespace geos::geom
389
} // namespace geos