Coverage Report

Created: 2025-11-08 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/include/geos/dissolve/LineDissolver.h
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (c) 2025 Martin Davis
7
 * Copyright (C) 2025 Paul Ramsey <pramsey@cleverelephant.ca>
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
#pragma once
17
18
#include <geos/dissolve/DissolveEdgeGraph.h>
19
// #include <memory>
20
// #include <vector>
21
// #include <stack>
22
#include <geos/geom/LineString.h>
23
#include <geos/export.h>
24
25
26
// Forward declarations
27
namespace geos {
28
namespace dissolve {
29
    class DissolveHalfEdge;
30
}
31
namespace edgegraph {
32
    class HalfEdge;
33
}
34
namespace geom {
35
    class CoordinateSequence;
36
    class Geometry;
37
    class GeometryFactory;
38
    // class LineString;
39
}
40
}
41
42
43
namespace geos {      // geos.
44
namespace dissolve {  // geos.dissolve
45
46
47
/**
48
 * Dissolves the linear components
49
 * from a collection of Geometry
50
 * into a set of maximal-length LineString
51
 * in which every unique segment appears once only.
52
 * The output linestrings run between node vertices
53
 * of the input, which are vertices which have
54
 * either degree 1, or degree 3 or greater.
55
 *
56
 * Use cases for dissolving linear components
57
 * include generalization
58
 * (in particular, simplifying polygonal coverages),
59
 * and visualization
60
 * (in particular, avoiding symbology conflicts when
61
 * depicting shared polygon boundaries).
62
 *
63
 * This class does **not** node the input lines.
64
 * If there are line segments crossing in the input,
65
 * they will still cross in the output.
66
 *
67
 * @author Martin Davis
68
 *
69
 */
70
class GEOS_DLL LineDissolver {
71
72
    using CoordinateSequence = geos::geom::CoordinateSequence;
73
    using Geometry = geos::geom::Geometry;
74
    using GeometryFactory = geos::geom::GeometryFactory;
75
    using LineString = geos::geom::LineString;
76
    using HalfEdge = geos::edgegraph::HalfEdge;
77
78
private:
79
80
    std::unique_ptr<Geometry> result;
81
    const GeometryFactory* factory = nullptr;
82
    DissolveEdgeGraph graph;
83
    std::vector<std::unique_ptr<LineString>> lines;
84
    std::stack<HalfEdge*> nodeEdgeStack;
85
    DissolveHalfEdge* ringStartEdge = nullptr;
86
87
88
    void computeResult();
89
90
    void process(HalfEdge* e);
91
92
    /**
93
     * Adds edges around this node to the stack.
94
     *
95
     * @param node
96
     */
97
    void stackEdges(HalfEdge* node);
98
99
    /**
100
     * For each edge in stack
101
     * (which must originate at a node)
102
     * extracts the line it initiates.
103
     */
104
    void buildLines();
105
106
    /**
107
     * Updates the tracked ringStartEdge
108
     * if the given edge has a lower origin
109
     * (using the standard Coordinate ordering).
110
     *
111
     * Identifying the lowest starting node meets two goals:
112
     *
113
     *  * It ensures that isolated input rings are created using the original node and orientation
114
     *  * For isolated rings formed from multiple input linestrings,
115
     *    it provides a canonical node and orientation for the output
116
     *    (rather than essentially random, and thus hard to test).
117
     *
118
     * @param e
119
     */
120
    void updateRingStartEdge(DissolveHalfEdge* e);
121
122
    /**
123
     * Builds a line starting from the given edge.
124
     * The start edge origin is a node (valence = 1 or >= 3),
125
     * unless it is part of a pure ring.
126
     * A pure ring has no other incident lines.
127
     * In this case the start edge may occur anywhere on the ring.
128
     *
129
     * The line is built up to the next node encountered,
130
     * or until the start edge is re-encountered
131
     * (which happens if the edges form a ring).
132
     *
133
     * @param eStart
134
     */
135
    void buildLine(HalfEdge* eStart);
136
137
    void buildRing(HalfEdge* eStartRing);
138
139
    void addLine(std::unique_ptr<CoordinateSequence>& cs);
140
141
142
public:
143
144
0
    LineDissolver() : result(nullptr) {};
145
146
    /**
147
     * Dissolves the linear components in a geometry.
148
     *
149
     * @param g the geometry to dissolve
150
     * @return the dissolved lines
151
     */
152
    static std::unique_ptr<Geometry> dissolve(const Geometry* g);
153
154
    /**
155
     * Adds a Geometry to be dissolved.
156
     * Any number of geometries may be added by calling this method multiple times.
157
     * Any type of Geometry may be added.  The constituent linework will be
158
     * extracted to be dissolved.
159
     *
160
     * @param geometry geometry to be line-merged
161
     */
162
    void add(const Geometry* geometry);
163
164
    /**
165
     * Adds a collection of Geometries to be processed. May be called multiple times.
166
     * Any dimension of Geometry may be added; the constituent linework will be
167
     * extracted.
168
     *
169
     * @param geometries the geometries to be line-merged
170
     */
171
    void add(std::vector<const Geometry*> geometries);
172
173
    void add(const LineString* lineString);
174
175
    /**
176
     * Gets the dissolved result as a MultiLineString.
177
     *
178
     * @return the dissolved lines
179
     */
180
    std::unique_ptr<Geometry> getResult();
181
182
    /**
183
     * Disable copy construction and assignment. Needed to make this
184
     * class compile under MSVC, because it has a vector<unique_ptr>
185
     * as a member. (See https://stackoverflow.com/q/29565299)
186
     */
187
    LineDissolver(const LineDissolver&) = delete;
188
    LineDissolver& operator=(const LineDissolver&) = delete;
189
190
191
};
192
193
} // namespace geos.dissolve
194
} // namespace geos
195