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