Coverage Report

Created: 2026-09-01 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/buffer/BufferInputLineSimplifier.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2009  Sandro Santilli <strk@kbt.io>
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
 * Last port: operation/buffer/BufferInputLineSimplifier.java r320 (JTS-1.12)
16
 *
17
 **********************************************************************/
18
19
#include <geos/operation/buffer/BufferInputLineSimplifier.h>
20
#include <geos/geom/CoordinateSequence.h> // for inlines
21
#include <geos/algorithm/Distance.h> // for use
22
#include <geos/algorithm/Orientation.h> // for use
23
#include <geos/util.h>
24
25
#include <memory>
26
#include <cmath>
27
#include <vector>
28
29
//#include <cassert>
30
31
using namespace geos::algorithm; // Orientation
32
using namespace geos::geom;
33
//using namespace geos::geomgraph; // DirectedEdge, Position
34
35
namespace geos {
36
namespace operation { // geos.operation
37
namespace buffer { // geos.operation.buffer
38
39
BufferInputLineSimplifier::BufferInputLineSimplifier(
40
    const geom::CoordinateSequence& input)
41
    :
42
67.9k
    inputLine(input),
43
67.9k
    angleOrientation(Orientation::COUNTERCLOCKWISE)
44
67.9k
{}
45
46
/*public static*/
47
std::unique_ptr<geom::CoordinateSequence>
48
BufferInputLineSimplifier::simplify(const geom::CoordinateSequence& inputLine,
49
                                    double distanceTol)
50
67.9k
{
51
67.9k
    BufferInputLineSimplifier simp(inputLine);
52
67.9k
    return simp.simplify(distanceTol);
53
67.9k
}
54
55
/* public */
56
std::unique_ptr<geom::CoordinateSequence>
57
BufferInputLineSimplifier::simplify(double nDistanceTol)
58
67.9k
{
59
67.9k
    distanceTol = fabs(nDistanceTol);
60
67.9k
    if(nDistanceTol < 0) {
61
33.6k
        angleOrientation = Orientation::CLOCKWISE;
62
33.6k
    }
63
64
    // rely on fact that boolean array is filled with false value
65
67.9k
    static const int startValue = INIT;
66
67.9k
    isDeleted.assign(inputLine.size(), startValue);
67
68
67.9k
    bool isChanged = false;
69
116k
    do {
70
116k
        isChanged = deleteShallowConcavities();
71
116k
    }
72
116k
    while(isChanged);
73
74
67.9k
    return collapseLine();
75
67.9k
}
76
77
/* private */
78
bool
79
BufferInputLineSimplifier::deleteShallowConcavities()
80
116k
{
81
    /*
82
     * Do not simplify end line segments of the line string.
83
     * This ensures that end caps are generated consistently.
84
     */
85
116k
    std::size_t index = 1;
86
87
116k
    auto midIndex = findNextNonDeletedIndex(index);
88
116k
    auto lastIndex = findNextNonDeletedIndex(midIndex);
89
90
116k
    bool isChanged = false;
91
4.11M
    while(lastIndex < inputLine.size()) {
92
        // test triple for shallow concavity
93
3.99M
        bool isMiddleVertexDeleted = false;
94
3.99M
        if(isDeletable(index, midIndex, lastIndex,
95
3.99M
                       distanceTol)) {
96
223k
            isDeleted[midIndex] = DELETE;
97
223k
            isMiddleVertexDeleted = true;
98
223k
            isChanged = true;
99
223k
        }
100
        // move simplification window forward
101
3.99M
        if(isMiddleVertexDeleted) {
102
223k
            index = lastIndex;
103
223k
        }
104
3.77M
        else {
105
3.77M
            index = midIndex;
106
3.77M
        }
107
108
3.99M
        midIndex = findNextNonDeletedIndex(index);
109
3.99M
        lastIndex = findNextNonDeletedIndex(midIndex);
110
3.99M
    }
111
116k
    return isChanged;
112
116k
}
113
114
/* private */
115
size_t
116
BufferInputLineSimplifier::findNextNonDeletedIndex(std::size_t index) const
117
8.22M
{
118
8.22M
    std::size_t next = index + 1;
119
8.22M
    const std::size_t len = inputLine.size();
120
9.28M
    while(next < len && isDeleted[next] == DELETE) {
121
1.06M
        next++;
122
1.06M
    }
123
8.22M
    return next;
124
8.22M
}
125
126
/* private */
127
std::unique_ptr<geom::CoordinateSequence>
128
BufferInputLineSimplifier::collapseLine() const
129
67.9k
{
130
67.9k
    auto coordList = new CoordinateSequence();
131
132
1.81M
    for(std::size_t i = 0, n = inputLine.size(); i < n; ++i) {
133
1.74M
        if(isDeleted[i] != DELETE) {
134
1.52M
            coordList->add(inputLine[i], false);
135
1.52M
        }
136
1.74M
    }
137
138
67.9k
    return std::unique_ptr<CoordinateSequence>(coordList);
139
67.9k
}
140
141
/* private */
142
bool
143
BufferInputLineSimplifier::isDeletable(std::size_t i0, std::size_t i1, std::size_t i2,
144
                                       double p_distanceTol) const
145
3.99M
{
146
3.99M
    const Coordinate& p0 = inputLine[i0];
147
3.99M
    const Coordinate& p1 = inputLine[i1];
148
3.99M
    const Coordinate& p2 = inputLine[i2];
149
150
3.99M
    if(! isConcave(p0, p1, p2)) {
151
2.66M
        return false;
152
2.66M
    }
153
1.32M
    if(! isShallow(p0, p1, p2, p_distanceTol)) {
154
1.01M
        return false;
155
1.01M
    }
156
157
    // MD - don't use this heuristic - it's too restricting
158
    // if (p0.distance(p2) > distanceTol) return false;
159
160
318k
    return isShallowSampled(p0, p1, i0, i2, p_distanceTol);
161
1.32M
}
162
163
/* private */
164
bool
165
BufferInputLineSimplifier::isShallowConcavity(const geom::Coordinate& p0,
166
        const geom::Coordinate& p1,
167
        const geom::Coordinate& p2,
168
        double p_distanceTol) const
169
0
{
170
0
    int orientation = Orientation::index(p0, p1, p2);
171
0
    bool isAngleToSimplify = (orientation == angleOrientation);
172
0
    if(! isAngleToSimplify) {
173
0
        return false;
174
0
    }
175
176
0
    double dist = Distance::pointToSegment(p1, p0, p2);
177
0
    return dist < p_distanceTol;
178
0
}
179
180
/* private */
181
bool
182
BufferInputLineSimplifier::isShallowSampled(const geom::Coordinate& p0,
183
        const geom::Coordinate& p2,
184
        std::size_t i0, std::size_t i2,
185
        double p_distanceTol) const
186
318k
{
187
    // check every n'th point to see if it is within tolerance
188
318k
    auto inc = (i2 - i0) / NUM_PTS_TO_CHECK;
189
318k
    if(inc <= 0) {
190
318k
        inc = 1;
191
318k
    }
192
193
852k
    for(std::size_t i = i0; i < i2; i += inc) {
194
629k
        if(! isShallow(p0, p2, inputLine[i], p_distanceTol)) {
195
95.5k
            return false;
196
95.5k
        }
197
629k
    }
198
223k
    return true;
199
318k
}
200
201
/* private */
202
bool
203
BufferInputLineSimplifier::isShallow(const geom::Coordinate& p0,
204
                                     const geom::Coordinate& p1,
205
                                     const geom::Coordinate& p2,
206
                                     double p_distanceTol) const
207
1.95M
{
208
1.95M
    double dist = Distance::pointToSegment(p1, p0, p2);
209
1.95M
    return dist < p_distanceTol;
210
1.95M
}
211
212
/* private */
213
bool
214
BufferInputLineSimplifier::isConcave(const geom::Coordinate& p0,
215
                                     const geom::Coordinate& p1,
216
                                     const geom::Coordinate& p2) const
217
3.99M
{
218
3.99M
    int orientation = Orientation::index(p0, p1, p2);
219
3.99M
    bool p_isConcave = (orientation == angleOrientation);
220
3.99M
    return p_isConcave;
221
3.99M
}
222
223
} // namespace geos.operation.buffer
224
} // namespace geos.operation
225
} // namespace geos
226