Coverage Report

Created: 2025-11-24 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/coverage/TPVWSimplifier.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2022 Paul Ramsey <pramsey@cleverelephant.ca>
7
 * Copyright (c) 2022 Martin Davis.
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Public Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************/
15
16
#include <geos/coverage/TPVWSimplifier.h>
17
#include <geos/coverage/Corner.h>
18
#include <geos/geom/Coordinate.h>
19
#include <geos/geom/CoordinateSequence.h>
20
#include <geos/geom/Envelope.h>
21
#include <geos/geom/Geometry.h>
22
#include <geos/geom/GeometryFactory.h>
23
#include <geos/geom/LineString.h>
24
#include <geos/geom/MultiLineString.h>
25
26
#include <geos/simplify/LinkedLine.h>
27
28
using geos::geom::Coordinate;
29
using geos::geom::CoordinateSequence;
30
using geos::geom::Envelope;
31
using geos::geom::Geometry;
32
using geos::geom::GeometryFactory;
33
using geos::geom::LineString;
34
using geos::geom::MultiLineString;
35
using geos::simplify::LinkedLine;
36
37
38
namespace geos {
39
namespace coverage { // geos.coverage
40
41
42
typedef TPVWSimplifier::Edge Edge;
43
typedef TPVWSimplifier::EdgeIndex EdgeIndex;
44
45
46
/* public static */
47
std::unique_ptr<MultiLineString>
48
TPVWSimplifier::simplify(
49
    const MultiLineString* lines,
50
    double distanceTolerance)
51
0
{
52
0
    TPVWSimplifier simp(lines, distanceTolerance);
53
0
    std::unique_ptr<MultiLineString> result = simp.simplify();
54
0
    return result;
55
0
}
56
57
58
/* public static */
59
std::unique_ptr<MultiLineString>
60
TPVWSimplifier::simplify(
61
    const MultiLineString* p_lines,
62
    std::vector<bool>& p_freeRings,
63
    const MultiLineString* p_constraintLines,
64
    double distanceTolerance)
65
0
{
66
0
    TPVWSimplifier simp(p_lines, distanceTolerance);
67
0
    simp.setFreeRingIndices(p_freeRings);
68
0
    simp.setConstraints(p_constraintLines);
69
0
    std::unique_ptr<MultiLineString> result = simp.simplify();
70
0
    return result;
71
0
}
72
73
74
/* public */
75
TPVWSimplifier::TPVWSimplifier(
76
    const MultiLineString* lines,
77
    double distanceTolerance)
78
0
    : inputLines(lines)
79
0
    , areaTolerance(distanceTolerance*distanceTolerance)
80
0
    , geomFactory(inputLines->getFactory())
81
0
    , constraintLines(nullptr)
82
0
    {}
83
84
85
/* private */
86
void
87
TPVWSimplifier::setConstraints(const MultiLineString* constraints)
88
0
{
89
0
    constraintLines = constraints;
90
0
}
91
92
/* public */
93
void
94
TPVWSimplifier::setFreeRingIndices(std::vector<bool>& freeRing)
95
0
{
96
    //XXX Assert: bit set has same size as number of lines.
97
0
    isFreeRing = freeRing;
98
0
}
99
100
/* private */
101
std::unique_ptr<MultiLineString>
102
TPVWSimplifier::simplify()
103
0
{
104
0
    std::vector<bool> emptyList;
105
0
    std::vector<Edge> edges = createEdges(inputLines, isFreeRing);
106
0
    std::vector<Edge> constraintEdges = createEdges(constraintLines, emptyList);
107
108
0
    EdgeIndex edgeIndex;
109
0
    edgeIndex.add(edges);
110
0
    edgeIndex.add(constraintEdges);
111
112
0
    std::vector<std::unique_ptr<LineString>> result;
113
0
    for (auto& edge : edges) {
114
0
        std::unique_ptr<CoordinateSequence> ptsSimp = edge.simplify(edgeIndex);
115
0
        auto ls = geomFactory->createLineString(std::move(ptsSimp));
116
0
        result.emplace_back(ls.release());
117
0
    }
118
0
    return geomFactory->createMultiLineString(std::move(result));
119
0
}
120
121
/* private */
122
std::vector<Edge>
123
TPVWSimplifier::createEdges(
124
    const MultiLineString* lines,
125
    std::vector<bool>& freeRing)
126
0
{
127
0
    std::vector<Edge> edges;
128
129
0
    if (lines == nullptr)
130
0
        return edges;
131
132
0
    for (std::size_t i = 0; i < lines->getNumGeometries(); i++) {
133
0
        const LineString* line = lines->getGeometryN(i);
134
0
        bool isFree = freeRing.empty() ? false : freeRing[i];
135
0
        edges.emplace_back(line, isFree, areaTolerance);
136
0
    }
137
0
    return edges;
138
0
}
139
140
/************************************************************************/
141
142
TPVWSimplifier::Edge::Edge(const LineString* p_inputLine, bool p_isFreeRing, double p_areaTolerance)
143
0
    : areaTolerance(p_areaTolerance)
144
0
    , isFreeRing(p_isFreeRing)
145
0
    , envelope(p_inputLine->getEnvelopeInternal())
146
0
    , nbPts(p_inputLine->getNumPoints())
147
0
    , linkedLine(*(p_inputLine->getCoordinatesRO()))
148
0
    , vertexIndex(*(p_inputLine->getCoordinatesRO()))
149
0
    , minEdgeSize(p_inputLine->getCoordinatesRO()->isRing() ? 3 : 2)
150
0
{
151
    // linkedLine = new LinkedLine(pts);
152
    // minEdgeSize = linkedLine.isRing() ? 3 : 2;
153
    // vertexIndex = new VertexSequencePackedRtree(pts);
154
    //-- remove ring duplicate final vertex
155
0
    if (linkedLine.isRing()) {
156
0
        vertexIndex.remove(nbPts-1);
157
0
    }
158
0
}
159
160
/* private */
161
const Coordinate&
162
TPVWSimplifier::Edge::getCoordinate(std::size_t index) const
163
0
{
164
0
    return linkedLine.getCoordinate(index);
165
0
}
166
167
/* public */
168
const Envelope*
169
TPVWSimplifier::Edge::getEnvelopeInternal() const
170
0
{
171
0
    return envelope;
172
0
}
173
174
/* public */
175
std::size_t
176
TPVWSimplifier::Edge::size() const
177
0
{
178
0
    return linkedLine.size();
179
0
}
180
181
/* private */
182
std::unique_ptr<CoordinateSequence>
183
TPVWSimplifier::Edge::simplify(EdgeIndex& edgeIndex)
184
0
{
185
0
    Corner::PriorityQueue cornerQueue;
186
0
    createQueue(cornerQueue);
187
0
    while (! cornerQueue.empty() && size() > minEdgeSize) {
188
        //Corner corner = cornerQueue.poll();
189
0
        Corner corner = cornerQueue.top();
190
0
        cornerQueue.pop();
191
192
        //-- a corner may no longer be valid due to removal of adjacent corners
193
0
        if (corner.isRemoved())
194
0
            continue;
195
        //System.out.println(corner.toLineString(edge));
196
        //-- done when all small corners are removed
197
0
        if (corner.getArea() > areaTolerance)
198
0
            break;
199
0
        if (isRemovable(corner, edgeIndex) ) {
200
0
            removeCorner(corner, cornerQueue);
201
0
        }
202
0
    }
203
0
    return linkedLine.getCoordinates();
204
0
}
205
206
/* private */
207
void
208
TPVWSimplifier::Edge::createQueue(Corner::PriorityQueue& cornerQueue)
209
0
{
210
0
    std::size_t minIndex = (linkedLine.isRing() && isFreeRing) ? 0 : 1;
211
0
    std::size_t maxIndex = nbPts - 1;
212
0
    for (std::size_t i = minIndex; i < maxIndex; i++) {
213
0
        addCorner(i, cornerQueue);
214
0
    }
215
0
    return;
216
0
}
217
218
/* private */
219
void
220
TPVWSimplifier::Edge::addCorner(
221
    std::size_t i,
222
    Corner::PriorityQueue& cornerQueue)
223
0
{
224
0
    if (isFreeRing || (i != 0 && i != nbPts-1)) {
225
0
        Corner corner(&linkedLine, i);
226
0
        if (corner.getArea() <= areaTolerance) {
227
0
            cornerQueue.push(corner);
228
0
        }
229
0
    }
230
0
}
231
232
/* private */
233
bool
234
TPVWSimplifier::Edge::isRemovable(
235
    Corner& corner,
236
    EdgeIndex& edgeIndex) const
237
0
{
238
0
    Envelope cornerEnv = corner.envelope();
239
    //-- check nearby lines for violating intersections
240
    //-- the query also returns this line for checking
241
0
    std::vector<const Edge*> edgeHits = edgeIndex.query(cornerEnv);
242
0
    for (const Edge* edge : edgeHits) {
243
0
        if (hasIntersectingVertex(corner, cornerEnv, *edge))
244
0
            return false;
245
    //-- check if corner base equals line (2-pts)
246
    //-- if so, don't remove corner, since that would collapse to the line
247
0
        if (edge != this && edge->size() == 2) {
248
            // TODO xxxxxx make linkedLine coordinates local
249
            // to linkedline and return a reference, update
250
            // simplify to clone reference at final step
251
0
            auto linePts = edge->linkedLine.getCoordinates();
252
0
            if (corner.isBaseline(linePts->getAt(0), linePts->getAt(1)))
253
0
                return false;
254
0
        }
255
0
    }
256
0
    return true;
257
0
}
258
259
260
/* private */
261
bool
262
TPVWSimplifier::Edge::hasIntersectingVertex(
263
    const Corner& corner,
264
    const Envelope& cornerEnv,
265
    const Edge& edge) const
266
0
{
267
0
    std::vector<std::size_t> result = edge.query(cornerEnv);
268
0
    for (std::size_t index : result) {
269
270
0
        const Coordinate& v = edge.getCoordinate(index);
271
        // ok if corner touches another line - should only happen at endpoints
272
0
        if (corner.isVertex(v))
273
0
            continue;
274
275
        //--- does corner triangle contain vertex?
276
0
        if (corner.intersects(v))
277
0
            return true;
278
0
    }
279
0
    return false;
280
0
}
281
282
/* private */
283
std::vector<std::size_t>
284
TPVWSimplifier::Edge::query(const Envelope& cornerEnv) const
285
0
{
286
0
    std::vector<std::size_t> result;
287
0
    vertexIndex.query(cornerEnv, result);
288
0
    return result;
289
0
}
290
291
292
/* private */
293
void
294
TPVWSimplifier::Edge::removeCorner(
295
    Corner& corner,
296
    Corner::PriorityQueue& cornerQueue)
297
0
{
298
0
    std::size_t index = corner.getIndex();
299
0
    std::size_t prev = linkedLine.prev(index);
300
0
    std::size_t next = linkedLine.next(index);
301
0
    linkedLine.remove(index);
302
0
    vertexIndex.remove(index);
303
304
    //-- potentially add the new corners created
305
0
    addCorner(prev, cornerQueue);
306
0
    addCorner(next, cornerQueue);
307
0
}
308
309
/************************************************************************/
310
311
312
/* public */
313
void
314
TPVWSimplifier::EdgeIndex::add(std::vector<Edge>& edges)
315
0
{
316
0
    for (Edge& edge : edges) {
317
0
        index.insert(&edge);
318
0
    }
319
0
}
320
321
/* public */
322
std::vector<const Edge*>
323
TPVWSimplifier::EdgeIndex::query(const Envelope& queryEnv)
324
0
{
325
0
    std::vector<const Edge*> hits;
326
0
    index.query(queryEnv, hits);
327
0
    return hits;
328
0
}
329
330
331
332
} // geos.coverage
333
} // geos