Skip to content

Commit 34a1e54

Browse files
authored
Port updates to BoundaryChainNoder (#1279)
Port of locationtech/jts#1134, to handle case where holes in a coverage touch at a point when generating a coverage union.
1 parent bdef327 commit 34a1e54

File tree

5 files changed

+228
-65
lines changed

5 files changed

+228
-65
lines changed

include/geos/noding/BoundaryChainNoder.h

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <geos/noding/Noder.h> // for composition
1818
#include <geos/noding/SegmentString.h> // for composition
1919
#include <geos/geom/LineSegment.h> // for composition
20+
#include <geos/noding/BasicSegmentString.h>
2021

2122
#include <unordered_set>
2223

@@ -57,7 +58,7 @@ class GEOS_DLL BoundaryChainNoder : public Noder {
5758

5859
private:
5960

60-
class BoundarySegmentMap {
61+
class BoundaryChainMap {
6162

6263
private:
6364

@@ -77,19 +78,19 @@ class GEOS_DLL BoundaryChainNoder : public Noder {
7778

7879
public:
7980

80-
BoundarySegmentMap(SegmentString* ss)
81+
BoundaryChainMap(SegmentString* ss)
8182
: segString(ss) {
8283
isBoundary.resize(ss->size()-1, false);
8384
};
8485

8586
void setBoundarySegment(std::size_t index);
86-
void createChains(std::vector<SegmentString*>& chainList, bool constructZ, bool constructM);
87+
void createChains(std::vector<SegmentString*>& chains, bool constructZ, bool constructM);
8788
};
8889

8990
class Segment {
9091
public:
9192
Segment(const geom::CoordinateSequence& seq,
92-
BoundarySegmentMap& segMap,
93+
BoundaryChainMap& segMap,
9394
std::size_t index)
9495
: m_seq(seq)
9596
, m_segMap(segMap)
@@ -105,7 +106,7 @@ class GEOS_DLL BoundaryChainNoder : public Noder {
105106
return m_seq.getAt<geom::CoordinateXY>(m_flip ? m_index + 1 : m_index);
106107
}
107108

108-
void markInBoundary() const {
109+
void markBoundary() const {
109110
m_segMap.setBoundarySegment(m_index);
110111
};
111112

@@ -125,15 +126,21 @@ class GEOS_DLL BoundaryChainNoder : public Noder {
125126

126127
private:
127128
const geom::CoordinateSequence& m_seq;
128-
BoundarySegmentMap& m_segMap;
129+
BoundaryChainMap& m_segMap;
129130
std::size_t m_index;
130131
bool m_flip;
131132
};
132133

134+
133135
public:
136+
134137
using SegmentSet = std::unordered_set<Segment, Segment::HashCode>;
135138

136-
BoundaryChainNoder() : chainList(nullptr), m_constructZ(false), m_constructM(false) {};
139+
BoundaryChainNoder()
140+
: m_chainList(nullptr)
141+
, m_constructZ(false)
142+
, m_constructM(false)
143+
{};
137144

138145
// Noder virtual methods
139146
std::vector<SegmentString*>* getNodedSubstrings() const override;
@@ -143,28 +150,53 @@ class GEOS_DLL BoundaryChainNoder : public Noder {
143150
private:
144151

145152
// Members
146-
std::vector<SegmentString*>* chainList;
153+
std::vector<SegmentString*>* m_chainList;
154+
std::vector<std::unique_ptr<geom::CoordinateSequence>> m_substrings;
147155
bool m_constructZ;
148156
bool m_constructM;
149157

150158
// Methods
151159
void addSegments(std::vector<SegmentString*>* segStrings,
152160
SegmentSet& segSet,
153-
std::vector<BoundarySegmentMap>& includedSegs);
161+
std::vector<BoundaryChainMap>& includedSegs);
154162

155163
static void addSegments(SegmentString* segString,
156-
BoundarySegmentMap& segInclude,
164+
BoundaryChainMap& segInclude,
157165
SegmentSet& segSet);
158166

167+
static bool segSetContains(
168+
SegmentSet& segSet, Segment& seg);
169+
159170
static void markBoundarySegments(SegmentSet& segSet);
160171

161-
std::vector<SegmentString*>* extractChains(std::vector<BoundarySegmentMap>& sections) const;
172+
std::vector<SegmentString*>* extractChains(std::vector<BoundaryChainMap>& sections) const;
173+
174+
Coordinate::UnorderedSet findNodePts(
175+
const std::vector<SegmentString*>* segStrings) const;
176+
177+
std::vector<SegmentString*>* nodeChains(
178+
const std::vector<SegmentString*>* chains,
179+
const Coordinate::UnorderedSet& nodePts);
162180

163-
static bool segSetContains(SegmentSet& segSet, Segment& seg);
181+
void nodeChain(
182+
SegmentString* chain,
183+
const Coordinate::UnorderedSet& nodePts,
184+
std::vector<SegmentString*>* nodedChains);
185+
186+
std::size_t findNodeIndex(
187+
const SegmentString* chain,
188+
std::size_t start,
189+
const Coordinate::UnorderedSet& nodePts) const;
190+
191+
noding::BasicSegmentString* substring(
192+
const SegmentString* segString,
193+
std::size_t start, std::size_t end);
194+
195+
// Declared as non-copyable
196+
BoundaryChainNoder(const BoundaryChainNoder& other);
197+
BoundaryChainNoder& operator=(const BoundaryChainNoder& rhs);
164198

165199
};
166200

167201
} // namespace geos::noding
168202
} // namespace geos
169-
170-

src/noding/BasicSegmentString.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,3 @@ BasicSegmentString::print(std::ostream& os) const
3838

3939
} // namespace geos.noding
4040
} // namespace geos
41-

src/noding/BoundaryChainNoder.cpp

Lines changed: 123 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,134 @@ namespace noding { // geos::noding
3131
void
3232
BoundaryChainNoder::computeNodes(std::vector<SegmentString*>* segStrings)
3333
{
34-
SegmentSet segSet;
35-
std::vector<BoundarySegmentMap> bdySections;
36-
bdySections.reserve(segStrings->size());
37-
addSegments(segStrings, segSet, bdySections);
38-
markBoundarySegments(segSet);
39-
chainList = extractChains(bdySections);
34+
SegmentSet boundarySegSet;
35+
std::vector<BoundaryChainMap> boundaryChains;
36+
boundaryChains.reserve(segStrings->size());
37+
addSegments(segStrings, boundarySegSet, boundaryChains);
38+
markBoundarySegments(boundarySegSet);
39+
m_chainList = extractChains(boundaryChains);
40+
41+
Coordinate::UnorderedSet nodePts = findNodePts(m_chainList);
42+
if (!nodePts.empty()) {
43+
std::vector<SegmentString*>* tmplist = nodeChains(m_chainList, nodePts);
44+
// At this point we have copied all the SegmentString*
45+
// we want to keep, so t container needs to go away and be replaced
46+
delete m_chainList;
47+
m_chainList = tmplist;
48+
}
49+
}
50+
51+
/* private */
52+
Coordinate::UnorderedSet
53+
BoundaryChainNoder::findNodePts(const std::vector<SegmentString*>* segStrings) const
54+
{
55+
Coordinate::UnorderedSet interiorVertices;
56+
Coordinate::UnorderedSet nodes;
57+
for (const SegmentString* ss : *segStrings) {
58+
//-- endpoints are nodes
59+
nodes.insert(ss->getCoordinate(0));
60+
nodes.insert(ss->getCoordinate(ss->size() - 1));
61+
62+
//-- check for duplicate interior points
63+
for (std::size_t i = 1; i < ss->size() - 1; i++) {
64+
const Coordinate& p = ss->getCoordinate(i);
65+
if (interiorVertices.find(p) != interiorVertices.end()) {
66+
nodes.insert(p);
67+
}
68+
interiorVertices.insert(p);
69+
}
70+
}
71+
return nodes;
72+
}
73+
74+
/* private */
75+
std::vector<SegmentString*>*
76+
BoundaryChainNoder::nodeChains(
77+
const std::vector<SegmentString*>* chains,
78+
const Coordinate::UnorderedSet& nodePts)
79+
{
80+
std::vector<SegmentString*>* nodedChains = new std::vector<SegmentString*>();
81+
for (SegmentString* chain : *chains) {
82+
nodeChain(chain, nodePts, nodedChains);
83+
}
84+
return nodedChains;
85+
}
86+
87+
88+
/* private */
89+
void
90+
BoundaryChainNoder::nodeChain(
91+
SegmentString* chain,
92+
const Coordinate::UnorderedSet& nodePts,
93+
std::vector<SegmentString*>* nodedChains)
94+
{
95+
std::size_t start = 0;
96+
while (start < chain->size() - 1) {
97+
std::size_t end = findNodeIndex(chain, start, nodePts);
98+
//-- if no interior nodes found, keep original chain
99+
if (start == 0 && end == chain->size() - 1) {
100+
nodedChains->push_back(chain);
101+
return;
102+
}
103+
nodedChains->push_back(substring(chain, start, end));
104+
start = end;
105+
}
106+
// We replaced this SegmentString with substrings,
107+
// and we are discarding the containing vector later
108+
// so get rid of this chain now
109+
delete chain;
110+
}
111+
112+
/* private static */
113+
BasicSegmentString*
114+
BoundaryChainNoder::substring(const SegmentString* segString, std::size_t start, std::size_t end)
115+
{
116+
// m_substrings.emplace_back(new CoordinateSequence());
117+
// CoordinateSequence* pts = m_substrings.back().get();
118+
CoordinateSequence* pts = new CoordinateSequence();
119+
for (std::size_t i = start; i < end + 1; i++) {
120+
pts->add(segString->getCoordinate(i));
121+
}
122+
return new BasicSegmentString(pts, segString->getData());
40123
}
41124

125+
126+
/* private */
127+
std::size_t
128+
BoundaryChainNoder::findNodeIndex(
129+
const SegmentString* chain,
130+
std::size_t start,
131+
const Coordinate::UnorderedSet& nodePts) const
132+
{
133+
for (std::size_t i = start + 1; i < chain->size(); i++) {
134+
if (nodePts.find(chain->getCoordinate(i)) != nodePts.end())
135+
return i;
136+
}
137+
return chain->size() - 1;
138+
}
139+
140+
42141
/* public */
43142
std::vector<SegmentString*>*
44143
BoundaryChainNoder::getNodedSubstrings() const
45144
{
46-
return chainList;
145+
return m_chainList;
47146
}
48147

49148
/* private */
50149
void
51150
BoundaryChainNoder::addSegments(
52151
std::vector<SegmentString*>* segStrings,
53152
SegmentSet& segSet,
54-
std::vector<BoundarySegmentMap>& includedSegs)
153+
std::vector<BoundaryChainMap>& boundaryChains)
55154
{
56155
for (SegmentString* ss : *segStrings) {
57156
m_constructZ |= ss->getCoordinates()->hasZ();
58157
m_constructM |= ss->getCoordinates()->hasM();
59158

60-
includedSegs.emplace_back(ss);
61-
BoundarySegmentMap& segInclude = includedSegs.back();
62-
addSegments(ss, segInclude, segSet);
159+
boundaryChains.emplace_back(ss);
160+
BoundaryChainMap& chainMap = boundaryChains.back();
161+
addSegments(ss, chainMap, segSet);
63162
}
64163
}
65164

@@ -80,13 +179,13 @@ BoundaryChainNoder::segSetContains(SegmentSet& segSet, Segment& seg)
80179
void
81180
BoundaryChainNoder::addSegments(
82181
SegmentString* segString,
83-
BoundarySegmentMap& segMap,
182+
BoundaryChainMap& chainMap,
84183
SegmentSet& segSet)
85184
{
86185
const CoordinateSequence& segCoords = *segString->getCoordinates();
87186

88187
for (std::size_t i = 0; i < segString->size() - 1; i++) {
89-
Segment seg(segCoords,segMap, i);
188+
Segment seg(segCoords, chainMap, i);
90189
if (segSetContains(segSet, seg)) {
91190
segSet.erase(seg);
92191
}
@@ -102,19 +201,19 @@ void
102201
BoundaryChainNoder::markBoundarySegments(SegmentSet& segSet)
103202
{
104203
for (const Segment& seg : segSet) {
105-
seg.markInBoundary();
204+
seg.markBoundary();
106205
}
107206
}
108207

109208
/* private */
110209
std::vector<SegmentString*>*
111-
BoundaryChainNoder::extractChains(std::vector<BoundarySegmentMap>& sections) const
210+
BoundaryChainNoder::extractChains(std::vector<BoundaryChainMap>& boundaryChains) const
112211
{
113-
std::vector<SegmentString*>* sectionList = new std::vector<SegmentString*>();
114-
for (BoundarySegmentMap& sect : sections) {
115-
sect.createChains(*sectionList, m_constructZ, m_constructM);
212+
std::vector<SegmentString*>* chains = new std::vector<SegmentString*>();
213+
for (BoundaryChainMap& chainMap : boundaryChains) {
214+
chainMap.createChains(*chains, m_constructZ, m_constructM);
116215
}
117-
return sectionList;
216+
return chains;
118217
}
119218

120219
/*************************************************************************
@@ -123,14 +222,14 @@ BoundaryChainNoder::extractChains(std::vector<BoundarySegmentMap>& sections) con
123222

124223
/* public */
125224
void
126-
BoundaryChainNoder::BoundarySegmentMap::setBoundarySegment(std::size_t index)
225+
BoundaryChainNoder::BoundaryChainMap::setBoundarySegment(std::size_t index)
127226
{
128227
isBoundary[index] = true;
129228
}
130229

131230
/* public */
132231
void
133-
BoundaryChainNoder::BoundarySegmentMap::createChains(
232+
BoundaryChainNoder::BoundaryChainMap::createChains(
134233
std::vector<SegmentString*>& chains,
135234
bool constructZ,
136235
bool constructM)
@@ -149,7 +248,7 @@ BoundaryChainNoder::BoundarySegmentMap::createChains(
149248

150249
/* private static */
151250
SegmentString*
152-
BoundaryChainNoder::BoundarySegmentMap::createChain(
251+
BoundaryChainNoder::BoundaryChainMap::createChain(
153252
const SegmentString* segString,
154253
std::size_t startIndex,
155254
std::size_t endIndex,
@@ -166,7 +265,7 @@ BoundaryChainNoder::BoundarySegmentMap::createChain(
166265

167266
/* private */
168267
std::size_t
169-
BoundaryChainNoder::BoundarySegmentMap::findChainStart(std::size_t index) const
268+
BoundaryChainNoder::BoundaryChainMap::findChainStart(std::size_t index) const
170269
{
171270
while (index < isBoundary.size() && ! isBoundary[index]) {
172271
index++;
@@ -176,7 +275,7 @@ BoundaryChainNoder::BoundarySegmentMap::findChainStart(std::size_t index) const
176275

177276
/* private */
178277
std::size_t
179-
BoundaryChainNoder::BoundarySegmentMap::findChainEnd(std::size_t index) const
278+
BoundaryChainNoder::BoundaryChainMap::findChainEnd(std::size_t index) const
180279
{
181280
index++;
182281
while (index < isBoundary.size() && isBoundary[index]) {

0 commit comments

Comments
 (0)