/src/geos/src/simplify/ComponentJumpChecker.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2006 Refractions Research Inc. |
7 | | * Copyright (C) 2023 Martin Davis <mtnclimb@gmail.com> |
8 | | * Copyright (C) 2023 Paul Ramsey <pramsey@cleverelephant.ca> |
9 | | * |
10 | | * This is free software; you can redistribute and/or modify it under |
11 | | * the terms of the GNU Lesser General Licence as published |
12 | | * by the Free Software Foundation. |
13 | | * See the COPYING file for more information. |
14 | | * |
15 | | **********************************************************************/ |
16 | | |
17 | | #include <geos/simplify/ComponentJumpChecker.h> |
18 | | #include <geos/simplify/TaggedLineString.h> |
19 | | |
20 | | #include <geos/algorithm/RayCrossingCounter.h> |
21 | | #include <geos/geom/Coordinate.h> |
22 | | #include <geos/geom/CoordinateSequence.h> |
23 | | #include <geos/geom/Envelope.h> |
24 | | #include <geos/geom/LineSegment.h> |
25 | | #include <geos/util.h> |
26 | | |
27 | | using geos::algorithm::RayCrossingCounter; |
28 | | using geos::geom::Coordinate; |
29 | | using geos::geom::CoordinateSequence; |
30 | | using geos::geom::Envelope; |
31 | | using geos::geom::LineSegment; |
32 | | |
33 | | |
34 | | namespace geos { |
35 | | namespace simplify { // geos::simplify |
36 | | |
37 | | /** |
38 | | * Checks if a line section jumps a component if flattened. |
39 | | * |
40 | | * Assumes start <= end. |
41 | | * |
42 | | * @param line the line containing the section being flattened |
43 | | * @param start start index of the section |
44 | | * @param end end index of the section |
45 | | * @param seg the flattening segment |
46 | | * @return true if the flattened section jumps a component |
47 | | */ |
48 | | /*public*/ |
49 | | bool |
50 | | ComponentJumpChecker::hasJump( |
51 | | const TaggedLineString* line, |
52 | | std::size_t start, std::size_t end, |
53 | | const LineSegment& seg) const |
54 | 0 | { |
55 | 0 | Envelope sectionEnv = computeEnvelope(line, start, end); |
56 | 0 | for (TaggedLineString* comp : components) { |
57 | | //-- don't test component against itself |
58 | 0 | if (comp == line) |
59 | 0 | continue; |
60 | | |
61 | 0 | const Coordinate& compPt = comp->getComponentPoint(); |
62 | 0 | if (sectionEnv.intersects(compPt)) { |
63 | 0 | if (hasJumpAtComponent(compPt, line, start, end, seg)) { |
64 | 0 | return true; |
65 | 0 | } |
66 | 0 | } |
67 | 0 | } |
68 | 0 | return false; |
69 | 0 | } |
70 | | |
71 | | |
72 | | /** |
73 | | * Checks if two consecutive segments jumps a component if flattened. |
74 | | * The segments are assumed to be consecutive. |
75 | | * (so the seg1.p1 = seg2.p0). |
76 | | * The flattening segment must be the segment between seg1.p0 and seg2.p1. |
77 | | * |
78 | | * @param line the line containing the section being flattened |
79 | | * @param seg1 the first replaced segment |
80 | | * @param seg2 the next replaced segment |
81 | | * @param seg the flattening segment |
82 | | * @return true if the flattened segment jumps a component |
83 | | */ |
84 | | /* public */ |
85 | | bool |
86 | | ComponentJumpChecker::hasJump( |
87 | | const TaggedLineString* line, |
88 | | const LineSegment* seg1, |
89 | | const LineSegment* seg2, |
90 | | const LineSegment& seg) const |
91 | 0 | { |
92 | 0 | Envelope sectionEnv = computeEnvelope(seg1, seg2); |
93 | 0 | for (TaggedLineString* comp : components) { |
94 | | //-- don't test component against itself |
95 | 0 | if (comp == line) |
96 | 0 | continue; |
97 | | |
98 | 0 | const Coordinate& compPt = comp->getComponentPoint(); |
99 | 0 | if (sectionEnv.intersects(compPt)) { |
100 | 0 | if (hasJumpAtComponent(compPt, seg1, seg2, seg)) { |
101 | 0 | return true; |
102 | 0 | } |
103 | 0 | } |
104 | 0 | } |
105 | 0 | return false; |
106 | 0 | } |
107 | | |
108 | | |
109 | | /*private static*/ |
110 | | bool |
111 | | ComponentJumpChecker::hasJumpAtComponent( |
112 | | const Coordinate& compPt, |
113 | | const TaggedLineString* line, |
114 | | std::size_t start, std::size_t end, |
115 | | const LineSegment& seg) |
116 | 0 | { |
117 | 0 | std::size_t sectionCount = crossingCount(compPt, line, start, end); |
118 | 0 | std::size_t segCount = crossingCount(compPt, seg); |
119 | 0 | bool hasJump = sectionCount % 2 != segCount % 2; |
120 | 0 | return hasJump; |
121 | 0 | } |
122 | | |
123 | | /*private static*/ |
124 | | bool |
125 | | ComponentJumpChecker::hasJumpAtComponent( |
126 | | const Coordinate& compPt, |
127 | | const LineSegment* seg1, const LineSegment* seg2, |
128 | | const LineSegment& seg) |
129 | 0 | { |
130 | 0 | std::size_t sectionCount = crossingCount(compPt, seg1, seg2); |
131 | 0 | std::size_t segCount = crossingCount(compPt, seg); |
132 | 0 | bool hasJump = sectionCount % 2 != segCount % 2; |
133 | 0 | return hasJump; |
134 | 0 | } |
135 | | |
136 | | /*private static*/ |
137 | | std::size_t |
138 | | ComponentJumpChecker::crossingCount( |
139 | | const Coordinate& compPt, |
140 | | const LineSegment& seg) |
141 | 0 | { |
142 | 0 | RayCrossingCounter rcc(compPt); |
143 | 0 | rcc.countSegment(seg.p0, seg.p1); |
144 | 0 | return rcc.getCount(); |
145 | 0 | } |
146 | | |
147 | | /*private static*/ |
148 | | std::size_t |
149 | | ComponentJumpChecker::crossingCount( |
150 | | const Coordinate& compPt, |
151 | | const LineSegment* seg1, const LineSegment* seg2) |
152 | 0 | { |
153 | 0 | RayCrossingCounter rcc(compPt); |
154 | 0 | rcc.countSegment(seg1->p0, seg1->p1); |
155 | 0 | rcc.countSegment(seg2->p0, seg2->p1); |
156 | 0 | return rcc.getCount(); |
157 | 0 | } |
158 | | |
159 | | /*private static*/ |
160 | | std::size_t |
161 | | ComponentJumpChecker::crossingCount( |
162 | | const Coordinate& compPt, |
163 | | const TaggedLineString* line, |
164 | | std::size_t start, std::size_t end) |
165 | 0 | { |
166 | 0 | RayCrossingCounter rcc(compPt); |
167 | 0 | for (std::size_t i = start; i < end; i++) { |
168 | 0 | rcc.countSegment(line->getCoordinate(i), line->getCoordinate(i + 1)); |
169 | 0 | } |
170 | 0 | return rcc.getCount(); |
171 | 0 | } |
172 | | |
173 | | /*private static*/ |
174 | | Envelope |
175 | | ComponentJumpChecker::computeEnvelope( |
176 | | const LineSegment* seg1, const LineSegment* seg2) |
177 | 0 | { |
178 | 0 | Envelope env; |
179 | 0 | env.expandToInclude(seg1->p0); |
180 | 0 | env.expandToInclude(seg1->p1); |
181 | 0 | env.expandToInclude(seg2->p0); |
182 | 0 | env.expandToInclude(seg2->p1); |
183 | 0 | return env; |
184 | 0 | } |
185 | | |
186 | | /*private static*/ |
187 | | Envelope |
188 | | ComponentJumpChecker::computeEnvelope( |
189 | | const TaggedLineString* line, |
190 | | std::size_t start, std::size_t end) |
191 | 0 | { |
192 | 0 | Envelope env; |
193 | 0 | for (std::size_t i = start; i <= end; i++) { |
194 | 0 | env.expandToInclude(line->getCoordinate(i)); |
195 | 0 | } |
196 | 0 | return env; |
197 | 0 | } |
198 | | |
199 | | |
200 | | |
201 | | } // namespace geos::simplify |
202 | | } // namespace geos |