/src/geos/src/operation/relateng/RelateEdge.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (c) 2024 Martin Davis |
7 | | * Copyright (C) 2024 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 | | #include <geos/algorithm/PolygonNodeTopology.h> |
17 | | #include <geos/operation/relateng/RelateEdge.h> |
18 | | #include <geos/operation/relateng/RelateNode.h> |
19 | | #include <geos/operation/relateng/RelateGeometry.h> |
20 | | #include <geos/geom/Coordinate.h> |
21 | | #include <geos/geom/Dimension.h> |
22 | | #include <geos/geom/Position.h> |
23 | | #include <geos/io/WKTWriter.h> |
24 | | #include <geos/constants.h> |
25 | | #include <sstream> |
26 | | |
27 | | using geos::algorithm::PolygonNodeTopology; |
28 | | using geos::geom::CoordinateXY; |
29 | | using geos::geom::Dimension; |
30 | | using geos::geom::Position; |
31 | | using geos::geom::Location; |
32 | | using geos::io::WKTWriter; |
33 | | |
34 | | |
35 | | namespace geos { // geos |
36 | | namespace operation { // geos.operation |
37 | | namespace relateng { // geos.operation.relateng |
38 | | |
39 | | |
40 | | /* public */ |
41 | | RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA, bool isForward) |
42 | 0 | : node(rNode) |
43 | 0 | , dirPt(pt) |
44 | 0 | { |
45 | 0 | setLocationsArea(isA, isForward); |
46 | 0 | } |
47 | | |
48 | | |
49 | | /* public */ |
50 | | RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA) |
51 | 0 | : node(rNode) |
52 | 0 | , dirPt(pt) |
53 | 0 | { |
54 | 0 | setLocationsLine(isA); |
55 | 0 | } |
56 | | |
57 | | |
58 | | /* public */ |
59 | | RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, |
60 | | bool isA, Location locLeft, Location locRight, Location locLine) |
61 | 0 | : node(rNode) |
62 | 0 | , dirPt(pt) |
63 | 0 | { |
64 | 0 | setLocations(isA, locLeft, locRight, locLine); |
65 | 0 | } |
66 | | |
67 | | |
68 | | /* public static */ |
69 | | RelateEdge * |
70 | | RelateEdge::create(const RelateNode* node, const CoordinateXY* dirPt, bool isA, int dim, bool isForward) |
71 | 0 | { |
72 | 0 | if (dim == Dimension::A) |
73 | | //-- create an area edge |
74 | 0 | return new RelateEdge(node, dirPt, isA, isForward); |
75 | | //-- create line edge |
76 | 0 | return new RelateEdge(node, dirPt, isA); |
77 | 0 | } |
78 | | |
79 | | |
80 | | /* public static */ |
81 | | std::size_t |
82 | | RelateEdge::findKnownEdgeIndex( |
83 | | std::vector<std::unique_ptr<RelateEdge>>& edges, |
84 | | bool isA) |
85 | 0 | { |
86 | 0 | for (std::size_t i = 0; i < edges.size(); i++) { |
87 | 0 | auto& e = edges[i]; |
88 | 0 | if (e->isKnown(isA)) |
89 | 0 | return i; |
90 | 0 | } |
91 | 0 | return INDEX_UNKNOWN; |
92 | 0 | } |
93 | | |
94 | | |
95 | | /* public static */ |
96 | | void |
97 | | RelateEdge::setAreaInterior( |
98 | | std::vector<std::unique_ptr<RelateEdge>>& edges, |
99 | | bool isA) |
100 | 0 | { |
101 | 0 | for (auto& e : edges) { |
102 | 0 | e->setAreaInterior(isA); |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | |
107 | | /* private */ |
108 | | void |
109 | | RelateEdge::setLocations(bool isA, Location locLeft, Location locRight, Location locLine) |
110 | 0 | { |
111 | 0 | if (isA) { |
112 | 0 | aDim = 2; |
113 | 0 | aLocLeft = locLeft; |
114 | 0 | aLocRight = locRight; |
115 | 0 | aLocLine = locLine; |
116 | 0 | } |
117 | 0 | else { |
118 | 0 | bDim = 2; |
119 | 0 | bLocLeft = locLeft; |
120 | 0 | bLocRight = locRight; |
121 | 0 | bLocLine = locLine; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | |
126 | | /* private */ |
127 | | void |
128 | | RelateEdge::setLocationsLine(bool isA) |
129 | 0 | { |
130 | 0 | if (isA) { |
131 | 0 | aDim = 1; |
132 | 0 | aLocLeft = Location::EXTERIOR; |
133 | 0 | aLocRight = Location::EXTERIOR; |
134 | 0 | aLocLine = Location::INTERIOR; |
135 | 0 | } |
136 | 0 | else { |
137 | 0 | bDim = 1; |
138 | 0 | bLocLeft = Location::EXTERIOR; |
139 | 0 | bLocRight = Location::EXTERIOR; |
140 | 0 | bLocLine = Location::INTERIOR; |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | |
145 | | /* private */ |
146 | | void |
147 | | RelateEdge::setLocationsArea(bool isA, bool isForward) |
148 | 0 | { |
149 | 0 | Location locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR; |
150 | 0 | Location locRight = isForward ? Location::INTERIOR : Location::EXTERIOR; |
151 | 0 | if (isA) { |
152 | 0 | aDim = 2; |
153 | 0 | aLocLeft = locLeft; |
154 | 0 | aLocRight = locRight; |
155 | 0 | aLocLine = Location::BOUNDARY; |
156 | 0 | } |
157 | 0 | else { |
158 | 0 | bDim = 2; |
159 | 0 | bLocLeft = locLeft; |
160 | 0 | bLocRight = locRight; |
161 | 0 | bLocLine = Location::BOUNDARY; |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | |
166 | | /* public */ |
167 | | int |
168 | | RelateEdge::compareToEdge(const CoordinateXY* edgeDirPt) const |
169 | 0 | { |
170 | 0 | return PolygonNodeTopology::compareAngle( |
171 | 0 | node->getCoordinate(), |
172 | 0 | dirPt, |
173 | 0 | edgeDirPt); |
174 | 0 | } |
175 | | |
176 | | |
177 | | /* public */ |
178 | | void |
179 | | RelateEdge::merge(bool isA, int dim, bool isForward) |
180 | 0 | { |
181 | 0 | Location locEdge = Location::INTERIOR; |
182 | 0 | Location locLeft = Location::EXTERIOR; |
183 | 0 | Location locRight = Location::EXTERIOR; |
184 | 0 | if (dim == Dimension::A) { |
185 | 0 | locEdge = Location::BOUNDARY; |
186 | 0 | locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR; |
187 | 0 | locRight = isForward ? Location::INTERIOR : Location::EXTERIOR; |
188 | 0 | } |
189 | |
|
190 | 0 | if (! isKnown(isA)) { |
191 | 0 | setDimension(isA, dim); |
192 | 0 | setOn(isA, locEdge); |
193 | 0 | setLeft(isA, locLeft); |
194 | 0 | setRight(isA, locRight); |
195 | 0 | return; |
196 | 0 | } |
197 | | |
198 | | // Assert: node-dirpt is collinear with node-pt |
199 | 0 | mergeDimEdgeLoc(isA, locEdge); |
200 | 0 | mergeSideLocation(isA, Position::LEFT, locLeft); |
201 | 0 | mergeSideLocation(isA, Position::RIGHT, locRight); |
202 | 0 | } |
203 | | |
204 | | /** |
205 | | * Area edges override Line edges. |
206 | | * Merging edges of same dimension is a no-op for |
207 | | * the dimension and on location. |
208 | | * But merging an area edge into a line edge |
209 | | * sets the dimension to A and the location to BOUNDARY. |
210 | | * |
211 | | * @param isA |
212 | | * @param locEdge |
213 | | */ |
214 | | /* private */ |
215 | | void |
216 | | RelateEdge::mergeDimEdgeLoc(bool isA, Location locEdge) |
217 | 0 | { |
218 | | //TODO: this logic needs work - ie handling A edges marked as Interior |
219 | 0 | int dim = (locEdge == Location::BOUNDARY) ? Dimension::A : Dimension::L; |
220 | 0 | if (dim == Dimension::A && dimension(isA) == Dimension::L) { |
221 | 0 | setDimension(isA, dim); |
222 | 0 | setOn(isA, Location::BOUNDARY); |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | |
227 | | /* private */ |
228 | | void |
229 | | RelateEdge::mergeSideLocation(bool isA, int pos, Location loc) |
230 | 0 | { |
231 | 0 | Location currLoc = location(isA, pos); |
232 | | //-- INTERIOR takes precedence over EXTERIOR |
233 | 0 | if (currLoc != Location::INTERIOR) { |
234 | 0 | setLocation(isA, pos, loc); |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | |
239 | | /* private */ |
240 | | void |
241 | | RelateEdge::setDimension(bool isA, int dimension) |
242 | 0 | { |
243 | 0 | if (isA) { |
244 | 0 | aDim = dimension; |
245 | 0 | } |
246 | 0 | else { |
247 | 0 | bDim = dimension; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | |
252 | | /* public */ |
253 | | void |
254 | | RelateEdge::setLocation(bool isA, int pos, Location loc) |
255 | 0 | { |
256 | 0 | switch (pos) { |
257 | 0 | case Position::LEFT: |
258 | 0 | setLeft(isA, loc); |
259 | 0 | break; |
260 | 0 | case Position::RIGHT: |
261 | 0 | setRight(isA, loc); |
262 | 0 | break; |
263 | 0 | case Position::ON: |
264 | 0 | setOn(isA, loc); |
265 | 0 | break; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | |
270 | | /* public */ |
271 | | void |
272 | | RelateEdge::setAllLocations(bool isA, Location loc) |
273 | 0 | { |
274 | 0 | setLeft(isA, loc); |
275 | 0 | setRight(isA, loc); |
276 | 0 | setOn(isA, loc); |
277 | 0 | } |
278 | | |
279 | | |
280 | | /* public */ |
281 | | void |
282 | | RelateEdge::setUnknownLocations(bool isA, Location loc) |
283 | 0 | { |
284 | 0 | if (! isKnown(isA, Position::LEFT)) { |
285 | 0 | setLocation(isA, Position::LEFT, loc); |
286 | 0 | } |
287 | 0 | if (! isKnown(isA, Position::RIGHT)) { |
288 | 0 | setLocation(isA, Position::RIGHT, loc); |
289 | 0 | } |
290 | 0 | if (! isKnown(isA, Position::ON)) { |
291 | 0 | setLocation(isA, Position::ON, loc); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | |
296 | | /* private */ |
297 | | void |
298 | | RelateEdge::setLeft(bool isA, Location loc) |
299 | 0 | { |
300 | 0 | if (isA) { |
301 | 0 | aLocLeft = loc; |
302 | 0 | } |
303 | 0 | else { |
304 | 0 | bLocLeft = loc; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | |
309 | | /* private */ |
310 | | void |
311 | | RelateEdge::setRight(bool isA, Location loc) |
312 | 0 | { |
313 | 0 | if (isA) { |
314 | 0 | aLocRight = loc; |
315 | 0 | } |
316 | 0 | else { |
317 | 0 | bLocRight = loc; |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | |
322 | | /* private */ |
323 | | void |
324 | | RelateEdge::setOn(bool isA, Location loc) |
325 | 0 | { |
326 | 0 | if (isA) { |
327 | 0 | aLocLine = loc; |
328 | 0 | } |
329 | 0 | else { |
330 | 0 | bLocLine = loc; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | |
335 | | /* public */ |
336 | | Location |
337 | | RelateEdge::location(bool isA, int position) const |
338 | 0 | { |
339 | 0 | if (isA) { |
340 | 0 | switch (position) { |
341 | 0 | case Position::LEFT: return aLocLeft; |
342 | 0 | case Position::RIGHT: return aLocRight; |
343 | 0 | case Position::ON: return aLocLine; |
344 | 0 | } |
345 | 0 | } |
346 | 0 | else { |
347 | 0 | switch (position) { |
348 | 0 | case Position::LEFT: return bLocLeft; |
349 | 0 | case Position::RIGHT: return bLocRight; |
350 | 0 | case Position::ON: return bLocLine; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | assert(false && "never get here"); |
354 | 0 | return LOC_UNKNOWN; |
355 | 0 | } |
356 | | |
357 | | /* private */ |
358 | | int |
359 | | RelateEdge::dimension(bool isA) const |
360 | 0 | { |
361 | 0 | return isA ? aDim : bDim; |
362 | 0 | } |
363 | | |
364 | | /* private */ |
365 | | bool |
366 | | RelateEdge::isKnown(bool isA) const |
367 | 0 | { |
368 | 0 | if (isA) |
369 | 0 | return aDim != DIM_UNKNOWN; |
370 | 0 | return bDim != DIM_UNKNOWN; |
371 | 0 | } |
372 | | |
373 | | /* private */ |
374 | | bool |
375 | | RelateEdge::isKnown(bool isA, int pos) const |
376 | 0 | { |
377 | 0 | return location(isA, pos) != LOC_UNKNOWN; |
378 | 0 | } |
379 | | |
380 | | |
381 | | /* public */ |
382 | | bool |
383 | | RelateEdge::isInterior(bool isA, int position) const |
384 | 0 | { |
385 | 0 | return location(isA, position) == Location::INTERIOR; |
386 | 0 | } |
387 | | |
388 | | |
389 | | /* public */ |
390 | | void |
391 | | RelateEdge::setDimLocations(bool isA, int dim, Location loc) |
392 | 0 | { |
393 | 0 | if (isA) { |
394 | 0 | aDim = dim; |
395 | 0 | aLocLeft = loc; |
396 | 0 | aLocRight = loc; |
397 | 0 | aLocLine = loc; |
398 | 0 | } |
399 | 0 | else { |
400 | 0 | bDim = dim; |
401 | 0 | bLocLeft = loc; |
402 | 0 | bLocRight = loc; |
403 | 0 | bLocLine = loc; |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | |
408 | | /* public */ |
409 | | void |
410 | | RelateEdge::setAreaInterior(bool isA) |
411 | 0 | { |
412 | 0 | if (isA) { |
413 | 0 | aLocLeft = Location::INTERIOR; |
414 | 0 | aLocRight = Location::INTERIOR; |
415 | 0 | aLocLine = Location::INTERIOR; |
416 | 0 | } |
417 | 0 | else { |
418 | 0 | bLocLeft = Location::INTERIOR; |
419 | 0 | bLocRight = Location::INTERIOR; |
420 | 0 | bLocLine = Location::INTERIOR; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | |
425 | | /* public */ |
426 | | std::string |
427 | | RelateEdge::toString() const |
428 | 0 | { |
429 | 0 | std::stringstream ss; |
430 | 0 | ss << WKTWriter::toLineString(*(node->getCoordinate()), *dirPt); |
431 | 0 | ss << " - " << labelString(); |
432 | 0 | return ss.str(); |
433 | 0 | } |
434 | | |
435 | | |
436 | | /* private */ |
437 | | std::string |
438 | | RelateEdge::labelString() const |
439 | 0 | { |
440 | 0 | std::stringstream ss; |
441 | 0 | ss << "A:"; |
442 | 0 | ss << locationString(RelateGeometry::GEOM_A); |
443 | 0 | ss << "/B:"; |
444 | 0 | ss << locationString(RelateGeometry::GEOM_B); |
445 | 0 | return ss.str(); |
446 | 0 | } |
447 | | |
448 | | |
449 | | /* private */ |
450 | | std::string |
451 | | RelateEdge::locationString(bool isA) const |
452 | 0 | { |
453 | 0 | std::stringstream ss; |
454 | 0 | ss << location(isA, Position::LEFT); |
455 | 0 | ss << location(isA, Position::ON); |
456 | 0 | ss << location(isA, Position::RIGHT); |
457 | 0 | return ss.str(); |
458 | 0 | } |
459 | | |
460 | | |
461 | | /* public friend */ |
462 | | std::ostream& |
463 | | operator<<(std::ostream& os, const RelateEdge& re) |
464 | 0 | { |
465 | 0 | os << re.toString(); |
466 | 0 | return os; |
467 | 0 | } |
468 | | |
469 | | |
470 | | } // namespace geos.operation.overlayng |
471 | | } // namespace geos.operation |
472 | | } // namespace geos |
473 | | |
474 | | |
475 | | |
476 | | |