1 /**********************************************************************
4 * GEOS - Geometry Engine Open Source
5 * http://geos.refractions.net
7 * Copyright (C) 2001-2002 Vivid Solutions Inc.
8 * Copyright (C) 2005 Refractions Research Inc.
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.
15 **********************************************************************
17 * Last port: geomgraph/GeometryGraph.java rev. 1.9 (JTS-1.10)
19 **********************************************************************/
21 #include <geos/algorithm/CGAlgorithms.h>
22 #include <geos/algorithm/BoundaryNodeRule.h>
24 #include <geos/util/UnsupportedOperationException.h>
26 #include <geos/geomgraph/GeometryGraph.h>
27 #include <geos/geomgraph/Node.h>
28 #include <geos/geomgraph/Edge.h>
29 #include <geos/geomgraph/Label.h>
30 #include <geos/geomgraph/Position.h>
32 #include <geos/geomgraph/index/SimpleMCSweepLineIntersector.h>
33 #include <geos/geomgraph/index/SegmentIntersector.h>
34 #include <geos/geomgraph/index/EdgeSetIntersector.h>
36 #include <geos/geom/CoordinateArraySequence.h>
37 #include <geos/geom/CoordinateSequence.h>
38 #include <geos/geom/Location.h>
39 #include <geos/geom/Point.h>
40 #include <geos/geom/LinearRing.h>
41 #include <geos/geom/LineString.h>
42 #include <geos/geom/Polygon.h>
43 #include <geos/geom/MultiPoint.h>
44 #include <geos/geom/MultiLineString.h>
45 #include <geos/geom/MultiPolygon.h>
46 #include <geos/geom/GeometryCollection.h>
48 #include <geos/inline.h>
51 #include <memory> // auto_ptr
60 # include "geos/geomgraph/GeometryGraph.inl"
64 using namespace geos::geomgraph::index
;
65 using namespace geos::algorithm
;
66 using namespace geos::geom
;
69 namespace geomgraph
{ // geos.geomgraph
72 * This method implements the Boundary Determination Rule
73 * for determining whether
74 * a component (node or edge) that appears multiple times in elements
75 * of a MultiGeometry is in the boundary or the interior of the Geometry
77 * The SFS uses the "Mod-2 Rule", which this function implements
79 * An alternative (and possibly more intuitive) rule would be
80 * the "At Most One Rule":
81 * isInBoundary = (componentCount == 1)
84 GeometryGraph::isInBoundary(int boundaryCount
)
87 return boundaryCount
%2==1;
91 GeometryGraph::determineBoundary(int boundaryCount
)
93 return isInBoundary(boundaryCount
)?Location::BOUNDARY
: Location::INTERIOR
;
98 GeometryGraph::createEdgeSetIntersector()
100 // various options for computing intersections, from slowest to fastest
102 //private EdgeSetIntersector esi = new SimpleEdgeSetIntersector();
103 //private EdgeSetIntersector esi = new MonotoneChainIntersector();
104 //private EdgeSetIntersector esi = new NonReversingChainIntersector();
105 //private EdgeSetIntersector esi = new SimpleSweepLineIntersector();
106 //private EdgeSetIntersector esi = new MCSweepLineIntersector();
108 //return new SimpleEdgeSetIntersector();
109 return new SimpleMCSweepLineIntersector();
114 GeometryGraph::getBoundaryNodes()
116 if ( ! boundaryNodes
.get() )
118 boundaryNodes
.reset(new vector
<Node
*>());
119 getBoundaryNodes(*(boundaryNodes
.get()));
121 return boundaryNodes
.get();
126 GeometryGraph::getBoundaryPoints()
129 if ( ! boundaryPoints
.get() )
131 // Collection will be destroied by GeometryGraph dtor
132 vector
<Node
*>* coll
= getBoundaryNodes();
133 boundaryPoints
.reset(new CoordinateArraySequence(coll
->size()));
135 for (vector
<Node
*>::iterator it
=coll
->begin(), endIt
=coll
->end();
139 boundaryPoints
->setAt(node
->getCoordinate(), i
++);
143 // We keep ownership of this, will be destroyed by destructor
144 return boundaryPoints
.get();
148 GeometryGraph::findEdge(const LineString
*line
)
150 return lineEdgeMap
.find(line
)->second
;
154 GeometryGraph::computeSplitEdges(vector
<Edge
*> *edgelist
)
157 cerr
<<"["<<this<<"] GeometryGraph::computeSplitEdges() scanning "<<edges
->size()<<" local and "<<edgelist
->size()<<" provided edges"<<endl
;
159 for (vector
<Edge
*>::iterator i
=edges
->begin(), endIt
=edges
->end();
164 cerr
<<" "<<e
->print()<<" adding split edges from arg"<<endl
;
166 e
->eiList
.addSplitEdges(edgelist
);
171 GeometryGraph::add(const Geometry
*g
)
172 //throw (UnsupportedOperationException *)
174 if (g
->isEmpty()) return;
176 // check if this Geometry should obey the Boundary Determination Rule
177 // all collections except MultiPolygons obey the rule
178 if ( dynamic_cast<const MultiPolygon
*>(g
) )
179 useBoundaryDeterminationRule
= false;
182 if ( const Polygon
* x
= dynamic_cast<const Polygon
*>(g
) )
185 // LineString also handles LinearRings
186 else if ( const LineString
* x
= dynamic_cast<const LineString
*>(g
) )
189 else if ( const Point
* x
= dynamic_cast<const Point
*>(g
) )
192 else if ( const GeometryCollection
* x
=
193 dynamic_cast<const GeometryCollection
*>(g
) )
199 string out
=typeid(*g
).name();
200 throw util::UnsupportedOperationException("GeometryGraph::add(Geometry *): unknown geometry type: "+out
);
205 GeometryGraph::addCollection(const GeometryCollection
*gc
)
207 for (size_t i
=0, n
=gc
->getNumGeometries(); i
<n
; ++i
)
209 const Geometry
*g
=gc
->getGeometryN(i
);
215 * Add a Point to the graph.
218 GeometryGraph::addPoint(const Point
*p
)
220 const Coordinate
& coord
=*(p
->getCoordinate());
221 insertPoint(argIndex
, coord
, Location::INTERIOR
);
225 * The left and right topological location arguments assume that the ring
227 * If the ring is in the opposite orientation,
228 * the left and right locations must be interchanged.
231 GeometryGraph::addPolygonRing(const LinearRing
*lr
, int cwLeft
, int cwRight
)
232 // throw IllegalArgumentException (see below)
234 // skip empty component (see bug #234)
235 if ( lr
->isEmpty() ) return;
237 const CoordinateSequence
*lrcl
= lr
->getCoordinatesRO();
239 CoordinateSequence
* coord
=CoordinateSequence::removeRepeatedPoints(lrcl
);
240 if (coord
->getSize()<4) {
241 hasTooFewPointsVar
=true;
242 invalidPoint
=coord
->getAt(0); // its now a Coordinate
250 * the isCCW call might throw an
251 * IllegalArgumentException if degenerate ring does
252 * not contain 3 distinct points.
256 if (CGAlgorithms::isCCW(coord
)) {
267 Edge
*e
=new Edge(coord
,new Label(argIndex
,Location::BOUNDARY
,left
,right
));
270 insertPoint(argIndex
,coord
->getAt(0), Location::BOUNDARY
);
274 GeometryGraph::addPolygon(const Polygon
*p
)
276 const LineString
* ls
;
277 const LinearRing
* lr
;
279 ls
= p
->getExteriorRing();
280 assert(dynamic_cast<const LinearRing
*>(ls
));
281 lr
= static_cast<const LinearRing
*>(ls
);
282 addPolygonRing(lr
, Location::EXTERIOR
, Location::INTERIOR
);
283 for (size_t i
=0, n
=p
->getNumInteriorRing(); i
<n
; ++i
)
285 // Holes are topologically labelled opposite to the shell, since
286 // the interior of the polygon lies on their opposite side
287 // (on the left, if the hole is oriented CW)
288 ls
= p
->getInteriorRingN(i
);
289 assert(dynamic_cast<const LinearRing
*>(ls
));
290 lr
= static_cast<const LinearRing
*>(ls
);
291 addPolygonRing(lr
, Location::INTERIOR
, Location::EXTERIOR
);
296 GeometryGraph::addLineString(const LineString
*line
)
298 CoordinateSequence
* coord
=CoordinateSequence::removeRepeatedPoints(line
->getCoordinatesRO());
299 if(coord
->getSize()<2) {
300 hasTooFewPointsVar
=true;
301 invalidPoint
=coord
->getAt(0);
306 Edge
*e
=new Edge(coord
,new Label(argIndex
,Location::INTERIOR
));
311 * Add the boundary points of the LineString, if any.
312 * Even if the LineString is closed, add both points as if they
314 * This allows for the case that the node already exists and is
317 assert(coord
->size() >= 2); // found LineString with single point
318 insertBoundaryPoint(argIndex
, coord
->getAt(0));
319 insertBoundaryPoint(argIndex
, coord
->getAt(coord
->getSize()-1));
323 * Add an Edge computed externally. The label on the Edge is assumed
327 GeometryGraph::addEdge(Edge
*e
)
330 const CoordinateSequence
* coord
=e
->getCoordinates();
331 // insert the endpoint as a node, to mark that it is on the boundary
332 insertPoint(argIndex
,coord
->getAt(0),Location::BOUNDARY
);
333 insertPoint(argIndex
,coord
->getAt(coord
->getSize()-1),Location::BOUNDARY
);
337 * Add a point computed externally. The point is assumed to be a
338 * Point Geometry part, which has a location of INTERIOR.
341 GeometryGraph::addPoint(Coordinate
& pt
)
343 insertPoint(argIndex
,pt
,Location::INTERIOR
);
348 GeometryGraph::computeSelfNodes(LineIntersector
*li
, bool computeRingSelfNodes
)
350 SegmentIntersector
*si
=new SegmentIntersector(li
,true,false);
351 auto_ptr
<EdgeSetIntersector
> esi(createEdgeSetIntersector());
353 // optimized test for Polygons and Rings
354 if (! computeRingSelfNodes
355 && ( dynamic_cast<const LinearRing
*>(parentGeom
)
356 || dynamic_cast<const Polygon
*>(parentGeom
)
357 || dynamic_cast<const MultiPolygon
*>(parentGeom
) ))
359 esi
->computeIntersections(edges
, si
, false);
363 esi
->computeIntersections(edges
, si
, true);
367 cerr
<< "SegmentIntersector # tests = " << si
->numTests
<< endl
;
370 addSelfIntersectionNodes(argIndex
);
375 GeometryGraph::computeEdgeIntersections(GeometryGraph
*g
,
376 LineIntersector
*li
, bool includeProper
)
379 cerr
<<"GeometryGraph::computeEdgeIntersections call"<<endl
;
381 SegmentIntersector
*si
=new SegmentIntersector(li
, includeProper
, true);
383 si
->setBoundaryNodes(getBoundaryNodes(), g
->getBoundaryNodes());
384 auto_ptr
<EdgeSetIntersector
> esi(createEdgeSetIntersector());
385 esi
->computeIntersections(edges
, g
->edges
, si
);
387 cerr
<<"GeometryGraph::computeEdgeIntersections returns"<<endl
;
393 GeometryGraph::insertPoint(int argIndex
, const Coordinate
& coord
,
397 cerr
<<"GeometryGraph::insertPoint("<<coord
.toString()<<" called"<<endl
;
399 Node
*n
=nodes
->addNode(coord
);
400 Label
*lbl
=n
->getLabel();
403 n
->setLabel(argIndex
, onLocation
);
407 lbl
->setLocation(argIndex
, onLocation
);
412 * Adds points using the mod-2 rule of SFS. This is used to add the boundary
413 * points of dim-1 geometries (Curves/MultiCurves). According to the SFS,
414 * an endpoint of a Curve is on the boundary
415 * iff if it is in the boundaries of an odd number of Geometries
418 GeometryGraph::insertBoundaryPoint(int argIndex
,const Coordinate
& coord
)
420 Node
*n
=nodes
->addNode(coord
);
421 Label
*lbl
=n
->getLabel();
423 // the new point to insert is on a boundary
426 // determine the current location for the point (if any)
427 if ( NULL
== lbl
) return;
429 int loc
= lbl
->getLocation(argIndex
,Position::ON
);
430 if (loc
==Location::BOUNDARY
) boundaryCount
++;
432 // determine the boundary status of the point according to the
433 // Boundary Determination Rule
434 int newLoc
= determineBoundary(boundaryNodeRule
, boundaryCount
);
435 lbl
->setLocation(argIndex
,newLoc
);
440 GeometryGraph::addSelfIntersectionNodes(int argIndex
)
442 for (vector
<Edge
*>::iterator i
=edges
->begin(), endIt
=edges
->end();
446 int eLoc
=e
->getLabel()->getLocation(argIndex
);
447 EdgeIntersectionList
&eiL
=e
->eiList
;
448 for (EdgeIntersectionList::iterator
449 eiIt
=eiL
.begin(), eiEnd
=eiL
.end();
452 EdgeIntersection
*ei
=*eiIt
;
453 addSelfIntersectionNode(argIndex
, ei
->coord
, eLoc
);
460 GeometryGraph::addSelfIntersectionNode(int argIndex
,
461 const Coordinate
& coord
, int loc
)
463 // if this node is already a boundary node, don't change it
464 if (isBoundaryNode(argIndex
,coord
)) return;
465 if (loc
==Location::BOUNDARY
&& useBoundaryDeterminationRule
)
467 insertBoundaryPoint(argIndex
,coord
);
471 insertPoint(argIndex
,coord
,loc
);
476 GeometryGraph::getEdges()
482 GeometryGraph::hasTooFewPoints()
484 return hasTooFewPointsVar
;
488 GeometryGraph::getInvalidPoint()
493 GeometryGraph::GeometryGraph(int newArgIndex
,
494 const geom::Geometry
*newParentGeom
)
497 parentGeom(newParentGeom
),
498 useBoundaryDeterminationRule(true),
499 boundaryNodeRule(algorithm::BoundaryNodeRule::OGC_SFS_BOUNDARY_RULE
),
500 argIndex(newArgIndex
),
501 hasTooFewPointsVar(false)
503 if (parentGeom
!=NULL
) add(parentGeom
);
506 GeometryGraph::GeometryGraph(int newArgIndex
,
507 const geom::Geometry
*newParentGeom
,
508 const algorithm::BoundaryNodeRule
& bnr
)
511 parentGeom(newParentGeom
),
512 useBoundaryDeterminationRule(true),
513 boundaryNodeRule(bnr
),
514 argIndex(newArgIndex
),
515 hasTooFewPointsVar(false)
517 if (parentGeom
!=NULL
) add(parentGeom
);
520 GeometryGraph::GeometryGraph()
524 useBoundaryDeterminationRule(true),
525 boundaryNodeRule(algorithm::BoundaryNodeRule::OGC_SFS_BOUNDARY_RULE
),
527 hasTooFewPointsVar(false)
534 GeometryGraph::determineBoundary(
535 const algorithm::BoundaryNodeRule
& boundaryNodeRule
,
538 return boundaryNodeRule
.isInBoundary(boundaryCount
)
539 ? Location::BOUNDARY
: Location::INTERIOR
;
542 } // namespace geos.geomgraph
545 /**********************************************************************
547 * Revision 1.30 2006/06/13 21:40:06 strk
548 * Cleanups and some more debugging lines
550 * Revision 1.29 2006/06/12 11:29:23 strk
551 * unsigned int => size_t
553 * Revision 1.28 2006/06/09 07:42:13 strk
554 * * source/geomgraph/GeometryGraph.cpp, source/operation/buffer/OffsetCurveSetBuilder.cpp, source/operation/overlay/OverlayOp.cpp, source/operation/valid/RepeatedPointTester.cpp: Fixed warning after Polygon ring accessor methods changed to work with size_t. Small optimizations in loops.
556 * Revision 1.27 2006/04/07 09:54:30 strk
557 * Geometry::getNumGeometries() changed to return 'unsigned int'
560 * Revision 1.26 2006/03/29 15:23:49 strk
561 * Moved GeometryGraph inlines from .h to .inl file
563 * Revision 1.25 2006/03/15 17:16:29 strk
564 * streamlined headers inclusion
566 **********************************************************************/