1 /**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
7 * Copyright (C) 2005-2006 Refractions Research Inc.
8 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9 *
10 * This is free software; you can redistribute and/or modify it under
11 * the terms of the GNU Lesser General Public Licence as published
12 * by the Free Software Foundation.
13 * See the COPYING file for more information.
14 *
15 **********************************************************************
16 *
17 * Last port: geomgraph/GeometryGraph.java r428 (JTS-1.12+)
18 *
19 **********************************************************************/
20
21 #include <geos/algorithm/Orientation.h>
22 #include <geos/algorithm/BoundaryNodeRule.h>
23
24 #include <geos/util/UnsupportedOperationException.h>
25 #include <geos/util.h>
26
27 #include <geos/geomgraph/GeometryGraph.h>
28 #include <geos/geomgraph/Node.h>
29 #include <geos/geomgraph/Edge.h>
30 #include <geos/geomgraph/Label.h>
31 #include <geos/geom/Position.h>
32
33 #include <geos/geomgraph/index/SimpleMCSweepLineIntersector.h>
34 #include <geos/geomgraph/index/SegmentIntersector.h>
35 #include <geos/geomgraph/index/EdgeSetIntersector.h>
36
37 #include <geos/geom/CoordinateArraySequence.h>
38 #include <geos/geom/CoordinateSequence.h>
39 #include <geos/geom/Location.h>
40 #include <geos/geom/Point.h>
41 #include <geos/geom/Envelope.h>
42 #include <geos/geom/LinearRing.h>
43 #include <geos/geom/LineString.h>
44 #include <geos/geom/Polygon.h>
45 #include <geos/geom/MultiPoint.h>
46 #include <geos/geom/MultiLineString.h>
47 #include <geos/geom/MultiPolygon.h>
48 #include <geos/geom/GeometryCollection.h>
49 #include <geos/util/Interrupt.h>
50
51 #include <geos/operation/valid/RepeatedPointRemover.h>
52
53 #include <geos/inline.h>
54
55 #include <vector>
56 #include <memory> // unique_ptr
57 #include <cassert>
58 #include <typeinfo>
59
60 #ifndef GEOS_DEBUG
61 #define GEOS_DEBUG 0
62 #endif
63
64 #ifndef GEOS_INLINE
65 # include "geos/geomgraph/GeometryGraph.inl"
66 #endif
67
68 using namespace std;
69 using namespace geos::geomgraph::index;
70 using namespace geos::algorithm;
71 using namespace geos::geom;
72
73 namespace geos {
74 namespace geomgraph { // geos.geomgraph
75
76 /*
77 * This method implements the Boundary Determination Rule
78 * for determining whether
79 * a component (node or edge) that appears multiple times in elements
80 * of a MultiGeometry is in the boundary or the interior of the Geometry
81 *
82 * The SFS uses the "Mod-2 Rule", which this function implements
83 *
84 * An alternative (and possibly more intuitive) rule would be
85 * the "At Most One Rule":
86 * isInBoundary = (componentCount == 1)
87 */
88 bool
isInBoundary(int boundaryCount)89 GeometryGraph::isInBoundary(int boundaryCount)
90 {
91 // the "Mod-2 Rule"
92 return boundaryCount % 2 == 1;
93 }
94
95 Location
determineBoundary(int boundaryCount)96 GeometryGraph::determineBoundary(int boundaryCount)
97 {
98 return isInBoundary(boundaryCount) ? Location::BOUNDARY : Location::INTERIOR;
99 }
100
101
102 EdgeSetIntersector*
createEdgeSetIntersector()103 GeometryGraph::createEdgeSetIntersector()
104 {
105 // various options for computing intersections, from slowest to fastest
106
107 //private EdgeSetIntersector esi = new SimpleEdgeSetIntersector();
108 //private EdgeSetIntersector esi = new MonotoneChainIntersector();
109 //private EdgeSetIntersector esi = new NonReversingChainIntersector();
110 //private EdgeSetIntersector esi = new SimpleSweepLineIntersector();
111 //private EdgeSetIntersector esi = new MCSweepLineIntersector();
112
113 //return new SimpleEdgeSetIntersector();
114 return new SimpleMCSweepLineIntersector();
115 }
116
117 /*public*/
118 vector<Node*>*
getBoundaryNodes()119 GeometryGraph::getBoundaryNodes()
120 {
121 if(! boundaryNodes.get()) {
122 boundaryNodes.reset(new vector<Node*>());
123 getBoundaryNodes(*(boundaryNodes.get()));
124 }
125 return boundaryNodes.get();
126 }
127
128 /*public*/
129 CoordinateSequence*
getBoundaryPoints()130 GeometryGraph::getBoundaryPoints()
131 {
132
133 if(! boundaryPoints.get()) {
134 // Collection will be destroied by GeometryGraph dtor
135 vector<Node*>* coll = getBoundaryNodes();
136 boundaryPoints.reset(new CoordinateArraySequence(coll->size()));
137 size_t i = 0;
138 for(vector<Node*>::iterator it = coll->begin(), endIt = coll->end();
139 it != endIt; ++it) {
140 Node* node = *it;
141 boundaryPoints->setAt(node->getCoordinate(), i++);
142 }
143 }
144
145 // We keep ownership of this, will be destroyed by destructor
146 return boundaryPoints.get();
147 }
148
149 Edge*
findEdge(const LineString * line) const150 GeometryGraph::findEdge(const LineString* line) const
151 {
152 return lineEdgeMap.find(line)->second;
153 }
154
155 void
computeSplitEdges(vector<Edge * > * edgelist)156 GeometryGraph::computeSplitEdges(vector<Edge*>* edgelist)
157 {
158 #if GEOS_DEBUG
159 cerr << "[" << this << "] GeometryGraph::computeSplitEdges() scanning " << edges->size() << " local and " <<
160 edgelist->size() << " provided edges" << endl;
161 #endif
162 for(vector<Edge*>::iterator i = edges->begin(), endIt = edges->end();
163 i != endIt; ++i) {
164 Edge* e = *i;
165 #if GEOS_DEBUG
166 cerr << " " << e->print() << " adding split edges from arg" << endl;
167 #endif
168 e->eiList.addSplitEdges(edgelist);
169 }
170 #if GEOS_DEBUG
171 cerr << "[" << this << "] GeometryGraph::computeSplitEdges() completed " << endl;
172 #endif
173 }
174
175 void
add(const Geometry * g)176 GeometryGraph::add(const Geometry* g)
177 //throw (UnsupportedOperationException *)
178 {
179 if(g->isEmpty()) {
180 return;
181 }
182
183 // check if this Geometry should obey the Boundary Determination Rule
184 // all collections except MultiPolygons obey the rule
185 if(dynamic_cast<const MultiPolygon*>(g)) {
186 useBoundaryDeterminationRule = false;
187 }
188
189
190 if(const Polygon* x1 = dynamic_cast<const Polygon*>(g)) {
191 addPolygon(x1);
192 }
193
194 // LineString also handles LinearRings
195 else if(const LineString* x2 = dynamic_cast<const LineString*>(g)) {
196 addLineString(x2);
197 }
198
199 else if(const Point* x3 = dynamic_cast<const Point*>(g)) {
200 addPoint(x3);
201 }
202
203 else if(const GeometryCollection* x4 =
204 dynamic_cast<const GeometryCollection*>(g)) {
205 addCollection(x4);
206 }
207
208 else {
209 string out = typeid(*g).name();
210 throw util::UnsupportedOperationException("GeometryGraph::add(Geometry *): unknown geometry type: " + out);
211 }
212 }
213
214 void
addCollection(const GeometryCollection * gc)215 GeometryGraph::addCollection(const GeometryCollection* gc)
216 {
217 for(size_t i = 0, n = gc->getNumGeometries(); i < n; ++i) {
218 const Geometry* g = gc->getGeometryN(i);
219 add(g);
220 }
221 }
222
223 /*
224 * Add a Point to the graph.
225 */
226 void
addPoint(const Point * p)227 GeometryGraph::addPoint(const Point* p)
228 {
229 const Coordinate& coord = *(p->getCoordinate());
230 insertPoint(argIndex, coord, Location::INTERIOR);
231 }
232
233 /*
234 * The left and right topological location arguments assume that the ring
235 * is oriented CW.
236 * If the ring is in the opposite orientation,
237 * the left and right locations must be interchanged.
238 */
239 void
addPolygonRing(const LinearRing * lr,Location cwLeft,Location cwRight)240 GeometryGraph::addPolygonRing(const LinearRing* lr, Location cwLeft, Location cwRight)
241 // throw IllegalArgumentException (see below)
242 {
243 // skip empty component (see bug #234)
244 if(lr->isEmpty()) {
245 return;
246 }
247
248 const CoordinateSequence* lrcl = lr->getCoordinatesRO();
249
250 auto coord = geos::operation::valid::RepeatedPointRemover::removeRepeatedPoints(lrcl);
251 if(coord->getSize() < 4) {
252 hasTooFewPointsVar = true;
253 invalidPoint = coord->getAt(0); // its now a Coordinate
254 return;
255 }
256 Location left = cwLeft;
257 Location right = cwRight;
258
259 /*
260 * the isCCW call might throw an
261 * IllegalArgumentException if degenerate ring does
262 * not contain 3 distinct points.
263 */
264 if(Orientation::isCCW(coord.get())) {
265 left = cwRight;
266 right = cwLeft;
267 }
268
269 auto coordRaw = coord.release();
270 Edge* e = new Edge(coordRaw, Label(argIndex, Location::BOUNDARY, left, right));
271 lineEdgeMap[lr] = e;
272 insertEdge(e);
273 insertPoint(argIndex, coordRaw->getAt(0), Location::BOUNDARY);
274 }
275
276 void
addPolygon(const Polygon * p)277 GeometryGraph::addPolygon(const Polygon* p)
278 {
279 const LinearRing* lr = p->getExteriorRing();
280
281 addPolygonRing(lr, Location::EXTERIOR, Location::INTERIOR);
282 for(size_t i = 0, n = p->getNumInteriorRing(); i < n; ++i) {
283 // Holes are topologically labelled opposite to the shell, since
284 // the interior of the polygon lies on their opposite side
285 // (on the left, if the hole is oriented CW)
286 lr = p->getInteriorRingN(i);
287 addPolygonRing(lr, Location::INTERIOR, Location::EXTERIOR);
288 }
289 }
290
291 void
addLineString(const LineString * line)292 GeometryGraph::addLineString(const LineString* line)
293 {
294 auto coord = operation::valid::RepeatedPointRemover::removeRepeatedPoints(line->getCoordinatesRO());
295 if(coord->getSize() < 2) {
296 hasTooFewPointsVar = true;
297 invalidPoint = coord->getAt(0);
298 return;
299 }
300
301 auto coordRaw = coord.release();
302 Edge* e = new Edge(coordRaw, Label(argIndex, Location::INTERIOR));
303 lineEdgeMap[line] = e;
304 insertEdge(e);
305
306 /*
307 * Add the boundary points of the LineString, if any.
308 * Even if the LineString is closed, add both points as if they
309 * were endpoints.
310 * This allows for the case that the node already exists and is
311 * a boundary point.
312 */
313 assert(coordRaw->size() >= 2); // found LineString with single point
314 insertBoundaryPoint(argIndex, coordRaw->getAt(0));
315 insertBoundaryPoint(argIndex, coordRaw->getAt(coordRaw->getSize() - 1));
316 }
317
318 /*
319 * Add an Edge computed externally. The label on the Edge is assumed
320 * to be correct.
321 */
322 void
addEdge(Edge * e)323 GeometryGraph::addEdge(Edge* e)
324 {
325 insertEdge(e);
326 const CoordinateSequence* coord = e->getCoordinates();
327 // insert the endpoint as a node, to mark that it is on the boundary
328 insertPoint(argIndex, coord->getAt(0), Location::BOUNDARY);
329 insertPoint(argIndex, coord->getAt(coord->getSize() - 1), Location::BOUNDARY);
330 }
331
332 /*
333 * Add a point computed externally. The point is assumed to be a
334 * Point Geometry part, which has a location of INTERIOR.
335 */
336 void
addPoint(Coordinate & pt)337 GeometryGraph::addPoint(Coordinate& pt)
338 {
339 insertPoint(argIndex, pt, Location::INTERIOR);
340 }
341
342 template <class T, class C>
343 void
collect_intersecting_edges(const Envelope * env,T start,T end,C & to)344 collect_intersecting_edges(const Envelope* env, T start, T end, C& to)
345 {
346 for(T i = start; i != end; ++i) {
347 Edge* e = *i;
348 if(e->getEnvelope()->intersects(env)) {
349 to.push_back(e);
350 }
351 }
352 }
353
354 /*public*/
355 std::unique_ptr<SegmentIntersector>
computeSelfNodes(LineIntersector & li,bool computeRingSelfNodes,const Envelope * env)356 GeometryGraph::computeSelfNodes(LineIntersector& li,
357 bool computeRingSelfNodes, const Envelope* env)
358 {
359 return computeSelfNodes(li, computeRingSelfNodes, false, env);
360 }
361
362 std::unique_ptr<SegmentIntersector>
computeSelfNodes(LineIntersector & li,bool computeRingSelfNodes,bool isDoneIfProperInt,const Envelope * env)363 GeometryGraph::computeSelfNodes(LineIntersector& li,
364 bool computeRingSelfNodes, bool isDoneIfProperInt, const Envelope* env)
365 {
366 auto si = detail::make_unique<SegmentIntersector>(&li, true, false);
367 si->setIsDoneIfProperInt(isDoneIfProperInt);
368 unique_ptr<EdgeSetIntersector> esi(createEdgeSetIntersector());
369
370 typedef vector<Edge*> EC;
371 EC* se = edges;
372 EC self_edges_copy;
373
374 if(env && ! env->covers(parentGeom->getEnvelopeInternal())) {
375 collect_intersecting_edges(env, se->begin(), se->end(), self_edges_copy);
376 //cerr << "(computeSelfNodes) Self edges reduced from " << se->size() << " to " << self_edges_copy.size() << endl;
377 se = &self_edges_copy;
378 }
379
380 bool isRings = dynamic_cast<const LinearRing*>(parentGeom)
381 || dynamic_cast<const Polygon*>(parentGeom)
382 || dynamic_cast<const MultiPolygon*>(parentGeom);
383
384 bool computeAllSegments = computeRingSelfNodes || ! isRings;
385
386 esi->computeIntersections(se, si.get(), computeAllSegments);
387
388 #if GEOS_DEBUG
389 cerr << "SegmentIntersector # tests = " << si->numTests << endl;
390 #endif // GEOS_DEBUG
391
392 addSelfIntersectionNodes(argIndex);
393 return si;
394 }
395
396 std::unique_ptr<SegmentIntersector>
computeEdgeIntersections(GeometryGraph * g,LineIntersector * li,bool includeProper,const Envelope * env)397 GeometryGraph::computeEdgeIntersections(GeometryGraph* g,
398 LineIntersector* li, bool includeProper, const Envelope* env)
399 {
400 #if GEOS_DEBUG
401 cerr << "GeometryGraph::computeEdgeIntersections call" << endl;
402 #endif
403 unique_ptr<SegmentIntersector> si(new SegmentIntersector(li, includeProper, true));
404
405 si->setBoundaryNodes(getBoundaryNodes(), g->getBoundaryNodes());
406 unique_ptr<EdgeSetIntersector> esi(createEdgeSetIntersector());
407
408 typedef vector<Edge*> EC;
409
410 EC self_edges_copy;
411 EC other_edges_copy;
412
413 EC* se = edges;
414 EC* oe = g->edges;
415 if(env && ! env->covers(parentGeom->getEnvelopeInternal())) {
416 collect_intersecting_edges(env, se->begin(), se->end(), self_edges_copy);
417 //cerr << "Self edges reduced from " << se->size() << " to " << self_edges_copy.size() << endl;
418 se = &self_edges_copy;
419 }
420 if(env && ! env->covers(g->parentGeom->getEnvelopeInternal())) {
421 collect_intersecting_edges(env, oe->begin(), oe->end(), other_edges_copy);
422 //cerr << "Other edges reduced from " << oe->size() << " to " << other_edges_copy.size() << endl;
423 oe = &other_edges_copy;
424 }
425 esi->computeIntersections(se, oe, si.get());
426 #if GEOS_DEBUG
427 cerr << "GeometryGraph::computeEdgeIntersections returns" << endl;
428 #endif
429 return si;
430 }
431
432 void
insertPoint(int p_argIndex,const Coordinate & coord,geom::Location onLocation)433 GeometryGraph::insertPoint(int p_argIndex, const Coordinate& coord,
434 geom::Location onLocation)
435 {
436 #if GEOS_DEBUG > 1
437 cerr << "GeometryGraph::insertPoint(" << coord.toString() << " called" << endl;
438 #endif
439 Node* n = nodes->addNode(coord);
440 Label& lbl = n->getLabel();
441 if(lbl.isNull()) {
442 n->setLabel(p_argIndex, onLocation);
443 }
444 else {
445 lbl.setLocation(p_argIndex, onLocation);
446 }
447 }
448
449 /*
450 * Adds points using the mod-2 rule of SFS. This is used to add the boundary
451 * points of dim-1 geometries (Curves/MultiCurves). According to the SFS,
452 * an endpoint of a Curve is on the boundary
453 * iff if it is in the boundaries of an odd number of Geometries
454 */
455 void
insertBoundaryPoint(int p_argIndex,const Coordinate & coord)456 GeometryGraph::insertBoundaryPoint(int p_argIndex, const Coordinate& coord)
457 {
458 Node* n = nodes->addNode(coord);
459 // nodes always have labels
460 Label& lbl = n->getLabel();
461
462 // the new point to insert is on a boundary
463 int boundaryCount = 1;
464
465 // determine the current location for the point (if any)
466 Location loc = lbl.getLocation(p_argIndex, Position::ON);
467 if(loc == Location::BOUNDARY) {
468 boundaryCount++;
469 }
470
471 // determine the boundary status of the point according to the
472 // Boundary Determination Rule
473 Location newLoc = determineBoundary(boundaryNodeRule, boundaryCount);
474 lbl.setLocation(p_argIndex, newLoc);
475 }
476
477 /*private*/
478 void
addSelfIntersectionNodes(int p_argIndex)479 GeometryGraph::addSelfIntersectionNodes(int p_argIndex)
480 {
481 for(vector<Edge*>::iterator i = edges->begin(), endIt = edges->end();
482 i != endIt; ++i) {
483 Edge* e = *i;
484 Location eLoc = e->getLabel().getLocation(p_argIndex);
485 EdgeIntersectionList& eiL = e->eiList;
486 for(const EdgeIntersection& ei : eiL) {
487 addSelfIntersectionNode(p_argIndex, ei.coord, eLoc);
488 GEOS_CHECK_FOR_INTERRUPTS();
489 }
490 }
491 }
492
493 /*private*/
494 void
addSelfIntersectionNode(int p_argIndex,const Coordinate & coord,Location loc)495 GeometryGraph::addSelfIntersectionNode(int p_argIndex,
496 const Coordinate& coord, Location loc)
497 {
498 // if this node is already a boundary node, don't change it
499 if(isBoundaryNode(p_argIndex, coord)) {
500 return;
501 }
502 if(loc == Location::BOUNDARY && useBoundaryDeterminationRule) {
503 insertBoundaryPoint(p_argIndex, coord);
504 }
505 else {
506 insertPoint(p_argIndex, coord, loc);
507 }
508 }
509
510 vector<Edge*>*
getEdges()511 GeometryGraph::getEdges()
512 {
513 return edges;
514 }
515
516 bool
hasTooFewPoints()517 GeometryGraph::hasTooFewPoints()
518 {
519 return hasTooFewPointsVar;
520 }
521
522 const Coordinate&
getInvalidPoint()523 GeometryGraph::getInvalidPoint()
524 {
525 return invalidPoint;
526 }
527
GeometryGraph(int newArgIndex,const geom::Geometry * newParentGeom)528 GeometryGraph::GeometryGraph(int newArgIndex,
529 const geom::Geometry* newParentGeom)
530 :
531 PlanarGraph(),
532 parentGeom(newParentGeom),
533 useBoundaryDeterminationRule(true),
534 boundaryNodeRule(algorithm::BoundaryNodeRule::getBoundaryOGCSFS()),
535 argIndex(newArgIndex),
536 hasTooFewPointsVar(false)
537 {
538 if(parentGeom != nullptr) {
539 add(parentGeom);
540 }
541 }
542
GeometryGraph(int newArgIndex,const geom::Geometry * newParentGeom,const algorithm::BoundaryNodeRule & bnr)543 GeometryGraph::GeometryGraph(int newArgIndex,
544 const geom::Geometry* newParentGeom,
545 const algorithm::BoundaryNodeRule& bnr)
546 :
547 PlanarGraph(),
548 parentGeom(newParentGeom),
549 useBoundaryDeterminationRule(true),
550 boundaryNodeRule(bnr),
551 argIndex(newArgIndex),
552 hasTooFewPointsVar(false)
553 {
554 if(parentGeom != nullptr) {
555 add(parentGeom);
556 }
557 }
558
GeometryGraph()559 GeometryGraph::GeometryGraph()
560 :
561 PlanarGraph(),
562 parentGeom(nullptr),
563 useBoundaryDeterminationRule(true),
564 boundaryNodeRule(algorithm::BoundaryNodeRule::getBoundaryOGCSFS()),
565 argIndex(-1),
566 hasTooFewPointsVar(false)
567 {
568 }
569
570
571 /* public static */
572 Location
determineBoundary(const algorithm::BoundaryNodeRule & boundaryNodeRule,int boundaryCount)573 GeometryGraph::determineBoundary(
574 const algorithm::BoundaryNodeRule& boundaryNodeRule,
575 int boundaryCount)
576 {
577 return boundaryNodeRule.isInBoundary(boundaryCount)
578 ? Location::BOUNDARY : Location::INTERIOR;
579 }
580
581 } // namespace geos.geomgraph
582 } // namespace geos
583
584