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