/src/geos/src/operation/buffer/BufferBuilder.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2009-2011 Sandro Santilli <strk@kbt.io> |
7 | | * Copyright (C) 2008-2010 Safe Software Inc. |
8 | | * Copyright (C) 2005-2007 Refractions Research Inc. |
9 | | * Copyright (C) 2001-2002 Vivid Solutions Inc. |
10 | | * |
11 | | * This is free software; you can redistribute and/or modify it under |
12 | | * the terms of the GNU Lesser General Public Licence as published |
13 | | * by the Free Software Foundation. |
14 | | * See the COPYING file for more information. |
15 | | * |
16 | | ********************************************************************** |
17 | | * |
18 | | * Last port: operation/buffer/BufferBuilder.java 149b38907 (JTS-1.12) |
19 | | * |
20 | | **********************************************************************/ |
21 | | |
22 | | #include <geos/geom/GeometryFactory.h> |
23 | | #include <geos/geom/Location.h> |
24 | | #include <geos/geom/Geometry.h> |
25 | | #include <geos/geom/Polygon.h> |
26 | | #include <geos/geom/GeometryCollection.h> |
27 | | #include <geos/geom/LineString.h> |
28 | | #include <geos/geom/MultiLineString.h> |
29 | | #include <geos/operation/buffer/BufferBuilder.h> |
30 | | #include <geos/operation/buffer/OffsetCurveBuilder.h> |
31 | | #include <geos/operation/buffer/BufferCurveSetBuilder.h> |
32 | | #include <geos/operation/buffer/BufferSubgraph.h> |
33 | | #include <geos/operation/buffer/SubgraphDepthLocater.h> |
34 | | #include <geos/operation/overlayng/OverlayNG.h> |
35 | | #include <geos/operation/overlay/snap/SnapOverlayOp.h> |
36 | | #include <geos/operation/buffer/PolygonBuilder.h> |
37 | | #include <geos/operation/buffer/BufferNodeFactory.h> |
38 | | #include <geos/operation/polygonize/Polygonizer.h> |
39 | | #include <geos/operation/union/UnaryUnionOp.h> |
40 | | #include <geos/operation/valid/RepeatedPointRemover.h> |
41 | | #include <geos/operation/linemerge/LineMerger.h> |
42 | | #include <geos/algorithm/LineIntersector.h> |
43 | | #include <geos/noding/IntersectionAdder.h> |
44 | | #include <geos/noding/SegmentString.h> |
45 | | #include <geos/noding/MCIndexNoder.h> |
46 | | #include <geos/noding/NodedSegmentString.h> |
47 | | #include <geos/geom/Position.h> |
48 | | #include <geos/geomgraph/PlanarGraph.h> |
49 | | #include <geos/geomgraph/Label.h> |
50 | | #include <geos/geomgraph/Node.h> |
51 | | #include <geos/geomgraph/Edge.h> |
52 | | #include <geos/util/GEOSException.h> |
53 | | #include <geos/io/WKTWriter.h> // for debugging |
54 | | #include <geos/util/IllegalArgumentException.h> |
55 | | #include <geos/profiler.h> |
56 | | #include <geos/util/Interrupt.h> |
57 | | |
58 | | #include <cassert> |
59 | | #include <vector> |
60 | | #include <iomanip> |
61 | | #include <algorithm> |
62 | | #include <iostream> |
63 | | |
64 | | // Debug single sided buffer |
65 | | //#define GEOS_DEBUG_SSB 1 |
66 | | |
67 | | #ifndef GEOS_DEBUG |
68 | | #define GEOS_DEBUG 0 |
69 | | #endif |
70 | | |
71 | | #ifndef JTS_DEBUG |
72 | | #define JTS_DEBUG 0 |
73 | | #endif |
74 | | |
75 | | // |
76 | | using namespace geos::geom; |
77 | | using namespace geos::geomgraph; |
78 | | using namespace geos::noding; |
79 | | using namespace geos::algorithm; |
80 | | using namespace geos::operation::linemerge; |
81 | | |
82 | | namespace { |
83 | | |
84 | | // Debug routine |
85 | | template <class Iterator> |
86 | | std::unique_ptr<Geometry> |
87 | | convertSegStrings(const GeometryFactory* fact, Iterator it, Iterator et) |
88 | | { |
89 | | std::vector<std::unique_ptr<Geometry>> lines; |
90 | | while(it != et) { |
91 | | const SegmentString* ss = *it; |
92 | | auto line = fact->createLineString(ss->getCoordinates()->clone()); |
93 | | lines.push_back(std::move(line)); |
94 | | ++it; |
95 | | } |
96 | | return fact->buildGeometry(lines.begin(), lines.end()); |
97 | | } |
98 | | |
99 | | } |
100 | | |
101 | | namespace geos { |
102 | | namespace operation { // geos.operation |
103 | | namespace buffer { // geos.operation.buffer |
104 | | |
105 | | #if PROFILE |
106 | | static Profiler* profiler = Profiler::instance(); |
107 | | #endif |
108 | | |
109 | | int |
110 | | BufferBuilder::depthDelta(const Label& label) |
111 | 0 | { |
112 | 0 | Location lLoc = label.getLocation(0, Position::LEFT); |
113 | 0 | Location rLoc = label.getLocation(0, Position::RIGHT); |
114 | 0 | if(lLoc == Location::INTERIOR && rLoc == Location::EXTERIOR) { |
115 | 0 | return 1; |
116 | 0 | } |
117 | 0 | else if(lLoc == Location::EXTERIOR && rLoc == Location::INTERIOR) { |
118 | 0 | return -1; |
119 | 0 | } |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | | BufferBuilder::~BufferBuilder() |
124 | 0 | { |
125 | 0 | delete li; // could be NULL |
126 | 0 | delete intersectionAdder; |
127 | 0 | } |
128 | | |
129 | | /*public*/ |
130 | | std::unique_ptr<Geometry> |
131 | | BufferBuilder::bufferLineSingleSided(const Geometry* g, double distance, |
132 | | bool leftSide) |
133 | 0 | { |
134 | | // Returns the line used to create a single-sided buffer. |
135 | | // Input requirement: Must be a LineString. |
136 | 0 | const LineString* l = dynamic_cast< const LineString* >(g); |
137 | 0 | if(!l) { |
138 | 0 | throw util::IllegalArgumentException("BufferBuilder::bufferLineSingleSided only accept linestrings"); |
139 | 0 | } |
140 | | |
141 | | // Nothing to do for a distance of zero |
142 | 0 | if(distance == 0) { |
143 | 0 | return g->clone(); |
144 | 0 | } |
145 | | |
146 | | // Get geometry factory and precision model. |
147 | 0 | const PrecisionModel* precisionModel = workingPrecisionModel; |
148 | 0 | if(!precisionModel) { |
149 | 0 | precisionModel = l->getPrecisionModel(); |
150 | 0 | } |
151 | |
|
152 | 0 | assert(precisionModel); |
153 | 0 | assert(l); |
154 | |
|
155 | 0 | geomFact = l->getFactory(); |
156 | | |
157 | | // First, generate the two-sided buffer using a butt-cap. |
158 | 0 | BufferParameters modParams = bufParams; |
159 | 0 | modParams.setEndCapStyle(BufferParameters::CAP_FLAT); |
160 | 0 | modParams.setSingleSided(false); // ignore parameter for areal-only geometries |
161 | | |
162 | | |
163 | | // This is a (temp?) hack to workaround the fact that |
164 | | // BufferBuilder BufferParameters are immutable after |
165 | | // construction, while we want to force the end cap |
166 | | // style to FLAT for single-sided buffering |
167 | 0 | BufferBuilder tmpBB(modParams); |
168 | 0 | std::unique_ptr<Geometry> buf(tmpBB.buffer(l, distance)); |
169 | | |
170 | | // Create MultiLineStrings from this polygon. |
171 | 0 | std::unique_ptr<Geometry> bufLineString(buf->getBoundary()); |
172 | |
|
173 | | #ifdef GEOS_DEBUG_SSB |
174 | | std::cerr << "input|" << *l << std::endl; |
175 | | std::cerr << "buffer|" << *bufLineString << std::endl; |
176 | | #endif |
177 | | |
178 | | // Then, get the raw (i.e. unnoded) single sided offset curve. |
179 | 0 | OffsetCurveBuilder curveBuilder(precisionModel, modParams); |
180 | 0 | std::vector< CoordinateSequence* > lineList; |
181 | |
|
182 | 0 | { |
183 | 0 | std::unique_ptr< CoordinateSequence > coords(g->getCoordinates()); |
184 | 0 | curveBuilder.getSingleSidedLineCurve(coords.get(), distance, |
185 | 0 | lineList, leftSide, !leftSide); |
186 | 0 | coords.reset(); |
187 | 0 | } |
188 | | |
189 | | // Create a SegmentString from these coordinates. |
190 | 0 | SegmentString::NonConstVect curveList; |
191 | 0 | for(unsigned int i = 0; i < lineList.size(); ++i) { |
192 | 0 | CoordinateSequence* seq = lineList[i]; |
193 | | |
194 | | // SegmentString takes ownership of CoordinateSequence |
195 | 0 | SegmentString* ss = new NodedSegmentString(seq, seq->hasZ(), seq->hasM(), nullptr); |
196 | 0 | curveList.push_back(ss); |
197 | 0 | } |
198 | 0 | lineList.clear(); |
199 | | |
200 | | |
201 | | // Node these SegmentStrings. |
202 | 0 | Noder* noder = getNoder(precisionModel); |
203 | 0 | noder->computeNodes(&curveList); |
204 | |
|
205 | 0 | SegmentString::NonConstVect* nodedEdges = noder->getNodedSubstrings(); |
206 | | |
207 | | // Create a geometry out of the noded substrings. |
208 | 0 | std::vector<std::unique_ptr<Geometry>> singleSidedNodedEdges; |
209 | 0 | singleSidedNodedEdges.reserve(nodedEdges->size()); |
210 | 0 | for(std::size_t i = 0, n = nodedEdges->size(); i < n; ++i) { |
211 | 0 | SegmentString* ss = (*nodedEdges)[i]; |
212 | |
|
213 | 0 | auto tmp = geomFact->createLineString(ss->getCoordinates()->clone()); |
214 | 0 | delete ss; |
215 | |
|
216 | 0 | singleSidedNodedEdges.push_back(std::move(tmp)); |
217 | 0 | } |
218 | |
|
219 | 0 | delete nodedEdges; |
220 | |
|
221 | 0 | for(std::size_t i = 0, n = curveList.size(); i < n; ++i) { |
222 | 0 | delete curveList[i]; |
223 | 0 | } |
224 | 0 | curveList.clear(); |
225 | |
|
226 | 0 | auto singleSided = geomFact->createMultiLineString(std::move(singleSidedNodedEdges)); |
227 | |
|
228 | | #ifdef GEOS_DEBUG_SSB |
229 | | std::cerr << "edges|" << *singleSided << std::endl; |
230 | | #endif |
231 | | |
232 | | // Use the boolean operation intersect to obtain the line segments lying |
233 | | // on both the butt-cap buffer and this multi-line. |
234 | | //Geometry* intersectedLines = singleSided->intersection( bufLineString ); |
235 | | // NOTE: we use Snapped overlay because the actual buffer boundary might |
236 | | // diverge from original offset curves due to the addition of |
237 | | // intersections with caps and joins curves |
238 | 0 | using geos::operation::overlay::snap::SnapOverlayOp; |
239 | 0 | std::unique_ptr<Geometry> intersectedLines = SnapOverlayOp::overlayOp(*singleSided, *bufLineString, |
240 | 0 | overlayng::OverlayNG::INTERSECTION); |
241 | |
|
242 | | #ifdef GEOS_DEBUG_SSB |
243 | | std::cerr << "intersection" << "|" << *intersectedLines << std::endl; |
244 | | #endif |
245 | | |
246 | | // Merge result lines together. |
247 | 0 | LineMerger lineMerge; |
248 | 0 | lineMerge.add(intersectedLines.get()); |
249 | 0 | auto mergedLines = lineMerge.getMergedLineStrings(); |
250 | | |
251 | | // Convert the result into a vector of geometries |
252 | 0 | std::vector<std::unique_ptr<Geometry>> mergedLinesGeom; |
253 | 0 | const Coordinate& startPoint = l->getCoordinatesRO()->front(); |
254 | 0 | const Coordinate& endPoint = l->getCoordinatesRO()->back(); |
255 | 0 | while(!mergedLines.empty()) { |
256 | | // Remove end points if they are a part of the original line to be |
257 | | // buffered. |
258 | 0 | CoordinateSequence::Ptr coords(mergedLines.back()->getCoordinates()); |
259 | 0 | if(nullptr != coords) { |
260 | | // Use 98% of the buffer width as the point-distance requirement - this |
261 | | // is to ensure that the point that is "distance" +/- epsilon is not |
262 | | // included. |
263 | | // |
264 | | // Let's try and estimate a more accurate bound instead of just assuming |
265 | | // 98%. With 98%, the epsilon grows as the buffer distance grows, |
266 | | // so that at large distance, artifacts may skip through this filter |
267 | | // Let the length of the line play a factor in the distance, which is still |
268 | | // going to be bounded by 98%. Take 10% of the length of the line from the buffer distance |
269 | | // to try and minimize any artifacts. |
270 | 0 | const double ptDistAllowance = std::max(distance - l->getLength() * 0.1, distance * 0.98); |
271 | | // Use 102% of the buffer width as the line-length requirement - this |
272 | | // is to ensure that line segments that is length "distance" +/- |
273 | | // epsilon is removed. |
274 | 0 | const double segLengthAllowance = 1.02 * distance; |
275 | |
|
276 | 0 | std::size_t front = 0; |
277 | 0 | std::size_t back = coords->size() - 1; |
278 | 0 | std::size_t sz = back - front + 1; |
279 | | |
280 | | // Clean up the front of the list. |
281 | | // Loop until the line's end is not inside the buffer width from |
282 | | // the startPoint. |
283 | 0 | while (sz > 1 && coords->getAt(front).distance(startPoint) < ptDistAllowance) { |
284 | | // Record the end segment length. |
285 | 0 | double segLength = coords->getAt(front).distance(coords->getAt(front + 1)); |
286 | | |
287 | | // Stop looping if there are no more points, or if the segment |
288 | | // length is larger than the buffer width. |
289 | 0 | if(segLength > segLengthAllowance) { |
290 | 0 | break; |
291 | 0 | } |
292 | | |
293 | | // If the first point is less than buffer width away from the |
294 | | // reference point, then delete the point. |
295 | 0 | front++; |
296 | 0 | sz--; |
297 | 0 | } |
298 | 0 | while(sz > 1 && coords->getAt(front).distance(endPoint) < ptDistAllowance) { |
299 | 0 | double segLength = coords->getAt(front).distance(coords->getAt(front + 1)); |
300 | 0 | if(segLength > segLengthAllowance) { |
301 | 0 | break; |
302 | 0 | } |
303 | 0 | front++; |
304 | 0 | sz--; |
305 | 0 | } |
306 | | // Clean up the back of the list. |
307 | 0 | while(sz > 1 && coords->getAt(back).distance(startPoint) < ptDistAllowance) { |
308 | 0 | double segLength = coords->getAt(back).distance(coords->getAt(back - 1)); |
309 | |
|
310 | 0 | if(segLength > segLengthAllowance) { |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | back--; |
314 | 0 | sz--; |
315 | 0 | } |
316 | 0 | while(sz > 1 && coords->getAt(back).distance(endPoint) < ptDistAllowance) { |
317 | 0 | double segLength = coords->getAt(back).distance(coords->getAt(back - 1)); |
318 | |
|
319 | 0 | if(segLength > segLengthAllowance) { |
320 | 0 | break; |
321 | 0 | } |
322 | 0 | back--; |
323 | 0 | sz--; |
324 | 0 | } |
325 | |
|
326 | 0 | if (sz > 1) { |
327 | 0 | if (sz < coords->size()) { |
328 | | // Points were removed; make a new CoordinateSequence |
329 | 0 | auto newSeq = detail::make_unique<CoordinateSequence>(sz, coords->getDimension()); |
330 | |
|
331 | 0 | for (std::size_t i = 0; i < sz; i++) { |
332 | 0 | newSeq->setAt(coords->getAt(i + front), i); |
333 | 0 | } |
334 | |
|
335 | 0 | coords = std::move(newSeq); |
336 | 0 | } |
337 | | |
338 | | // Add the coordinates to the resultant line string. |
339 | 0 | mergedLinesGeom.push_back(geomFact->createLineString(std::move(coords))); |
340 | 0 | } |
341 | 0 | } |
342 | |
|
343 | 0 | mergedLines.pop_back(); |
344 | 0 | } |
345 | | |
346 | | // Clean up. |
347 | 0 | if(noder != workingNoder) { |
348 | 0 | delete noder; |
349 | 0 | } |
350 | 0 | buf.reset(); |
351 | 0 | singleSided.reset(); |
352 | 0 | intersectedLines.reset(); |
353 | |
|
354 | 0 | if(mergedLinesGeom.size() > 1) { |
355 | 0 | return geomFact->createMultiLineString(std::move(mergedLinesGeom)); |
356 | 0 | } |
357 | 0 | else if(mergedLinesGeom.size() == 1) { |
358 | 0 | std::unique_ptr<Geometry> single = std::move(mergedLinesGeom[0]); |
359 | 0 | return single; |
360 | 0 | } |
361 | 0 | else { |
362 | 0 | return geomFact->createLineString(); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | /*public*/ |
367 | | std::unique_ptr<Geometry> |
368 | | BufferBuilder::buffer(const Geometry* g, double distance) |
369 | | // throw(GEOSException *) |
370 | 0 | { |
371 | | // Single-sided buffer only works on single geometries |
372 | | // so we'll need to do it individually and then union |
373 | | // the result |
374 | 0 | if ( bufParams.isSingleSided() && g->getNumGeometries() > 1 ) |
375 | 0 | { |
376 | 0 | std::vector< std::unique_ptr<Geometry> > geoms_to_delete; |
377 | 0 | for ( size_t i=0, n=g->getNumGeometries(); i<n; ++i ) |
378 | 0 | { |
379 | | // BufferBuilder class cannot be reused, so |
380 | | // we create a new one for each subgeom |
381 | 0 | BufferBuilder subbuilder(bufParams); |
382 | 0 | const Geometry *subgeom = g->getGeometryN(i); |
383 | 0 | std::unique_ptr<Geometry> subbuf = subbuilder.buffer(subgeom, distance); |
384 | 0 | geoms_to_delete.push_back( std::move(subbuf) ); |
385 | 0 | } |
386 | 0 | std::vector< Geometry* > geoms; |
387 | 0 | for ( size_t i=0, n=geoms_to_delete.size(); i<n; ++i ) |
388 | 0 | geoms.push_back( geoms_to_delete[i].get() ); |
389 | 0 | return operation::geounion::UnaryUnionOp::Union(geoms); |
390 | 0 | } |
391 | | |
392 | 0 | const PrecisionModel* precisionModel = workingPrecisionModel; |
393 | 0 | if(precisionModel == nullptr) { |
394 | 0 | precisionModel = g->getPrecisionModel(); |
395 | 0 | } |
396 | |
|
397 | 0 | assert(precisionModel); |
398 | 0 | assert(g); |
399 | | |
400 | | // factory must be the same as the one used by the input |
401 | 0 | geomFact = g->getFactory(); |
402 | |
|
403 | 0 | { |
404 | | // This scope is here to force release of resources owned by |
405 | | // BufferCurveSetBuilder when we're doing with it |
406 | 0 | BufferCurveSetBuilder curveSetBuilder(*g, distance, precisionModel, bufParams); |
407 | 0 | curveSetBuilder.setInvertOrientation(isInvertOrientation); |
408 | |
|
409 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
410 | |
|
411 | 0 | std::vector<SegmentString*>& bufferSegStrList = curveSetBuilder.getCurves(); |
412 | |
|
413 | | #if GEOS_DEBUG |
414 | | std::cerr << "BufferCurveSetBuilder got " << bufferSegStrList.size() |
415 | | << " curves" << std::endl; |
416 | | #endif |
417 | | // short-circuit test |
418 | 0 | if(bufferSegStrList.empty()) { |
419 | 0 | return createEmptyResultGeometry(); |
420 | 0 | } |
421 | | |
422 | | #if GEOS_DEBUG |
423 | | std::cerr << "BufferBuilder::buffer computing NodedEdges" << std::endl; |
424 | | #endif |
425 | | |
426 | 0 | computeNodedEdges(bufferSegStrList, precisionModel); |
427 | |
|
428 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
429 | |
|
430 | 0 | } // bufferSegStrList and contents are released here |
431 | | |
432 | | #if GEOS_DEBUG > 1 |
433 | | std::cerr << std::endl << edgeList << std::endl; |
434 | | #endif |
435 | | |
436 | 0 | std::unique_ptr<Geometry> resultGeom(nullptr); |
437 | 0 | std::vector<std::unique_ptr<Geometry>> resultPolyList; |
438 | 0 | std::vector<BufferSubgraph*> subgraphList; |
439 | |
|
440 | 0 | try { |
441 | 0 | PlanarGraph graph(BufferNodeFactory::instance()); |
442 | 0 | graph.addEdges(edgeList.getEdges()); |
443 | |
|
444 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
445 | |
|
446 | 0 | createSubgraphs(&graph, subgraphList); |
447 | |
|
448 | | #if GEOS_DEBUG |
449 | | std::cerr << "Created " << subgraphList.size() << " subgraphs" << std::endl; |
450 | | #if GEOS_DEBUG > 1 |
451 | | for(std::size_t i = 0, n = subgraphList.size(); i < n; i++) { |
452 | | std::cerr << std::setprecision(10) << *(subgraphList[i]) << std::endl; |
453 | | } |
454 | | #endif |
455 | | #endif |
456 | |
|
457 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
458 | |
|
459 | 0 | { |
460 | | // scope for earlier PolygonBuilder cleanup |
461 | 0 | PolygonBuilder polyBuilder(geomFact); |
462 | 0 | buildSubgraphs(subgraphList, polyBuilder); |
463 | |
|
464 | 0 | resultPolyList = polyBuilder.getPolygons(); |
465 | 0 | } |
466 | | |
467 | | // Get rid of the subgraphs, shouldn't be needed anymore |
468 | 0 | for(std::size_t i = 0, n = subgraphList.size(); i < n; i++) { |
469 | 0 | delete subgraphList[i]; |
470 | 0 | } |
471 | 0 | subgraphList.clear(); |
472 | |
|
473 | | #if GEOS_DEBUG |
474 | | std::cerr << "PolygonBuilder got " << resultPolyList.size() |
475 | | << " polygons" << std::endl; |
476 | | #if GEOS_DEBUG > 1 |
477 | | for(std::size_t i = 0, n = resultPolyList.size(); i < n; i++) { |
478 | | std::cerr << resultPolyList[i]->toString() << std::endl; |
479 | | } |
480 | | #endif |
481 | | #endif |
482 | | |
483 | | // just in case ... |
484 | 0 | if(resultPolyList.empty()) { |
485 | 0 | return createEmptyResultGeometry(); |
486 | 0 | } |
487 | | |
488 | | // resultPolyList ownership transferred here |
489 | 0 | resultGeom = geomFact->buildGeometry(std::move(resultPolyList)); |
490 | |
|
491 | 0 | } |
492 | 0 | catch(const util::GEOSException& /* exc */) { |
493 | | |
494 | | // In case they're still around |
495 | 0 | for(std::size_t i = 0, n = subgraphList.size(); i < n; i++) { |
496 | 0 | delete subgraphList[i]; |
497 | 0 | } |
498 | 0 | subgraphList.clear(); |
499 | |
|
500 | 0 | throw; |
501 | 0 | } |
502 | | |
503 | | // Cleanup single-sided buffer artifacts, if needed |
504 | 0 | if ( bufParams.isSingleSided() ) |
505 | 0 | { |
506 | | |
507 | | // Get linework of input geom |
508 | 0 | const Geometry *inputLineString = g; |
509 | 0 | std::unique_ptr<Geometry> inputPolygonBoundary; |
510 | 0 | if ( g->getDimension() > 1 ) |
511 | 0 | { |
512 | 0 | inputPolygonBoundary = g->getBoundary(); |
513 | 0 | inputLineString = inputPolygonBoundary.get(); |
514 | 0 | } |
515 | |
|
516 | | #if GEOS_DEBUG |
517 | | std::cerr << "Input linework: " << *inputLineString << std::endl; |
518 | | #endif |
519 | | |
520 | | // Get linework of buffer geom |
521 | 0 | std::unique_ptr<Geometry> bufferBoundary = resultGeom->getBoundary(); |
522 | |
|
523 | | #if GEOS_DEBUG |
524 | | std::cerr << "Buffer boundary: " << *bufferBoundary << std::endl; |
525 | | #endif |
526 | | |
527 | | // Node all linework |
528 | 0 | using operation::overlayng::OverlayNG; |
529 | 0 | std::unique_ptr<Geometry> nodedLinework = OverlayNG::overlay(inputLineString, bufferBoundary.get(), OverlayNG::UNION); |
530 | |
|
531 | | #if GEOS_DEBUG |
532 | | std::cerr << "Noded linework: " << *nodedLinework << std::endl; |
533 | | #endif |
534 | |
|
535 | 0 | using operation::polygonize::Polygonizer; |
536 | 0 | Polygonizer plgnzr; |
537 | 0 | plgnzr.add(nodedLinework.get()); |
538 | 0 | std::vector<std::unique_ptr<geom::Polygon>> polys = plgnzr.getPolygons(); |
539 | |
|
540 | | #if GEOS_DEBUG |
541 | | std::cerr << "Polygonization of noded linework returned: " << polys.size() << " polygons" << std::endl; |
542 | | #endif |
543 | |
|
544 | 0 | if ( polys.size() > 1 ) |
545 | 0 | { |
546 | | // Only keep larger polygon |
547 | 0 | std::vector<std::unique_ptr<geom::Polygon>>::iterator |
548 | 0 | it, |
549 | 0 | itEnd = polys.end(), |
550 | 0 | biggestPolygonIterator = itEnd; |
551 | 0 | double maxArea = 0; |
552 | 0 | for ( it = polys.begin(); it != itEnd; ++it ) |
553 | 0 | { |
554 | 0 | double area = (*it)->getArea(); |
555 | 0 | if ( area > maxArea ) |
556 | 0 | { |
557 | 0 | biggestPolygonIterator = it; |
558 | 0 | maxArea = area; |
559 | 0 | } |
560 | 0 | } |
561 | |
|
562 | 0 | if ( biggestPolygonIterator != itEnd ) { |
563 | 0 | Geometry *gg = (*biggestPolygonIterator).release(); |
564 | 0 | return std::unique_ptr<Geometry>( gg ); |
565 | 0 | } // else there were no polygons formed... |
566 | 0 | } |
567 | |
|
568 | 0 | } |
569 | | |
570 | 0 | return resultGeom; |
571 | 0 | } |
572 | | |
573 | | /*private*/ |
574 | | Noder* |
575 | | BufferBuilder::getNoder(const PrecisionModel* pm) |
576 | 0 | { |
577 | | // this doesn't change workingNoder precisionModel! |
578 | 0 | if(workingNoder != nullptr) { |
579 | 0 | return workingNoder; |
580 | 0 | } |
581 | | |
582 | | // otherwise use a fast (but non-robust) noder |
583 | | |
584 | 0 | if(li) { // reuse existing IntersectionAdder and LineIntersector |
585 | 0 | li->setPrecisionModel(pm); |
586 | 0 | assert(intersectionAdder != nullptr); |
587 | 0 | } |
588 | 0 | else { |
589 | 0 | li = new LineIntersector(pm); |
590 | 0 | intersectionAdder = new IntersectionAdder(*li); |
591 | 0 | } |
592 | |
|
593 | 0 | MCIndexNoder* noder = new MCIndexNoder(intersectionAdder); |
594 | |
|
595 | | #if 0 |
596 | | /* CoordinateArraySequence.cpp:84: |
597 | | * virtual const geos::Coordinate& geos::CoordinateArraySequence::getAt(std::size_t) const: |
598 | | * Assertion `pos<vect->size()' failed. |
599 | | */ |
600 | | |
601 | | Noder* noder = new IteratedNoder(pm); |
602 | | |
603 | | Noder noder = new MCIndexSnapRounder(pm); |
604 | | Noder noder = new ScaledNoder( |
605 | | new MCIndexSnapRounder(new PrecisionModel(1.0)), |
606 | | pm.getScale()); |
607 | | #endif |
608 | |
|
609 | 0 | return noder; |
610 | |
|
611 | 0 | } |
612 | | |
613 | | /* private */ |
614 | | void |
615 | | BufferBuilder::computeNodedEdges(SegmentString::NonConstVect& bufferSegStrList, |
616 | | const PrecisionModel* precisionModel) // throw(GEOSException) |
617 | 0 | { |
618 | 0 | Noder* noder = getNoder(precisionModel); |
619 | |
|
620 | | #if JTS_DEBUG |
621 | | geos::io::WKTWriter wktWriter; |
622 | | std::cerr << "before noding: " |
623 | | << wktWriter.write( |
624 | | convertSegStrings(geomFact, bufferSegStrList.begin(), |
625 | | bufferSegStrList.end()).get() |
626 | | ) << std::endl; |
627 | | #endif |
628 | |
|
629 | 0 | noder->computeNodes(&bufferSegStrList); |
630 | |
|
631 | 0 | SegmentString::NonConstVect* nodedSegStrings = \ |
632 | 0 | noder->getNodedSubstrings(); |
633 | |
|
634 | | #if JTS_DEBUG |
635 | | std::cerr << "after noding: " |
636 | | << wktWriter.write( |
637 | | convertSegStrings(geomFact, bufferSegStrList.begin(), |
638 | | bufferSegStrList.end()).get() |
639 | | ) << std::endl; |
640 | | #endif |
641 | | |
642 | |
|
643 | 0 | for(SegmentString::NonConstVect::iterator |
644 | 0 | i = nodedSegStrings->begin(), e = nodedSegStrings->end(); |
645 | 0 | i != e; |
646 | 0 | ++i) { |
647 | 0 | SegmentString* segStr = *i; |
648 | 0 | const Label* oldLabel = static_cast<const Label*>(segStr->getData()); |
649 | |
|
650 | 0 | auto cs = operation::valid::RepeatedPointRemover::removeRepeatedPoints(segStr->getCoordinates()); |
651 | 0 | delete segStr; |
652 | 0 | if(cs->size() < 2) { |
653 | | // don't insert collapsed edges |
654 | | // we need to take care of the memory here as cs is a new sequence |
655 | 0 | continue; |
656 | 0 | } |
657 | | |
658 | | // Edge takes ownership of the CoordinateSequence |
659 | 0 | Edge* edge = new Edge(cs.release(), *oldLabel); |
660 | | |
661 | | // will take care of the Edge ownership |
662 | 0 | insertUniqueEdge(edge); |
663 | 0 | } |
664 | |
|
665 | 0 | delete nodedSegStrings; |
666 | |
|
667 | 0 | if(noder != workingNoder) { |
668 | 0 | delete noder; |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | | /*private*/ |
673 | | void |
674 | | BufferBuilder::insertUniqueEdge(Edge* e) |
675 | 0 | { |
676 | | //<FIX> MD 8 Oct 03 speed up identical edge lookup |
677 | | // fast lookup |
678 | 0 | Edge* existingEdge = edgeList.findEqualEdge(e); |
679 | | // If an identical edge already exists, simply update its label |
680 | 0 | if(existingEdge != nullptr) { |
681 | 0 | Label& existingLabel = existingEdge->getLabel(); |
682 | 0 | Label labelToMerge = e->getLabel(); |
683 | | |
684 | | // check if new edge is in reverse direction to existing edge |
685 | | // if so, must flip the label before merging it |
686 | 0 | if(! existingEdge->isPointwiseEqual(e)) { |
687 | 0 | labelToMerge = e->getLabel(); |
688 | 0 | labelToMerge.flip(); |
689 | 0 | } |
690 | |
|
691 | 0 | existingLabel.merge(labelToMerge); |
692 | | |
693 | | // compute new depth delta of sum of edges |
694 | 0 | int mergeDelta = depthDelta(labelToMerge); |
695 | 0 | int existingDelta = existingEdge->getDepthDelta(); |
696 | 0 | int newDelta = existingDelta + mergeDelta; |
697 | 0 | existingEdge->setDepthDelta(newDelta); |
698 | | |
699 | | // we have memory release responsibility |
700 | 0 | delete e; |
701 | |
|
702 | 0 | } |
703 | 0 | else { // no matching existing edge was found |
704 | | |
705 | | // add this new edge to the list of edges in this graph |
706 | 0 | edgeList.add(e); |
707 | |
|
708 | 0 | e->setDepthDelta(depthDelta(e->getLabel())); |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | | bool |
713 | | BufferSubgraphGT(BufferSubgraph* first, BufferSubgraph* second) |
714 | 0 | { |
715 | 0 | if(first->compareTo(second) > 0) { |
716 | 0 | return true; |
717 | 0 | } |
718 | 0 | else { |
719 | 0 | return false; |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | | /*private*/ |
724 | | void |
725 | | BufferBuilder::createSubgraphs(PlanarGraph* graph, std::vector<BufferSubgraph*>& subgraphList) |
726 | 0 | { |
727 | 0 | std::vector<Node*> nodes; |
728 | 0 | graph->getNodes(nodes); |
729 | 0 | for(std::size_t i = 0, n = nodes.size(); i < n; i++) { |
730 | 0 | Node* node = nodes[i]; |
731 | 0 | if(!node->isVisited()) { |
732 | 0 | BufferSubgraph* subgraph = new BufferSubgraph(); |
733 | 0 | subgraph->create(node); |
734 | 0 | subgraphList.push_back(subgraph); |
735 | 0 | } |
736 | 0 | } |
737 | | |
738 | | /* |
739 | | * Sort the subgraphs in descending order of their rightmost coordinate |
740 | | * This ensures that when the Polygons for the subgraphs are built, |
741 | | * subgraphs for shells will have been built before the subgraphs for |
742 | | * any holes they contain |
743 | | */ |
744 | 0 | std::sort(subgraphList.begin(), subgraphList.end(), BufferSubgraphGT); |
745 | 0 | } |
746 | | |
747 | | /*private*/ |
748 | | void |
749 | | BufferBuilder::buildSubgraphs(const std::vector<BufferSubgraph*>& subgraphList, |
750 | | PolygonBuilder& polyBuilder) |
751 | 0 | { |
752 | |
|
753 | | #if GEOS_DEBUG |
754 | | std::cerr << __FUNCTION__ << " got " << subgraphList.size() << " subgraphs" << std::endl; |
755 | | #endif |
756 | 0 | std::vector<BufferSubgraph*> processedGraphs; |
757 | 0 | for(std::size_t i = 0, n = subgraphList.size(); i < n; i++) { |
758 | 0 | BufferSubgraph* subgraph = subgraphList[i]; |
759 | 0 | Coordinate* p = subgraph->getRightmostCoordinate(); |
760 | 0 | assert(p); |
761 | |
|
762 | | #if GEOS_DEBUG |
763 | | std::cerr << " " << i << ") Subgraph[" << subgraph << "]" << std::endl; |
764 | | std::cerr << " rightmost Coordinate " << *p; |
765 | | #endif |
766 | 0 | SubgraphDepthLocater locater(&processedGraphs); |
767 | | #if GEOS_DEBUG |
768 | | std::cerr << " after SubgraphDepthLocater processedGraphs contain " |
769 | | << processedGraphs.size() |
770 | | << " elements" << std::endl; |
771 | | #endif |
772 | 0 | int outsideDepth = locater.getDepth(*p); |
773 | | #if GEOS_DEBUG |
774 | | std::cerr << " Depth of rightmost coordinate: " << outsideDepth << std::endl; |
775 | | #endif |
776 | 0 | subgraph->computeDepth(outsideDepth); |
777 | 0 | subgraph->findResultEdges(); |
778 | | #if GEOS_DEBUG |
779 | | std::cerr << " after computeDepth and findResultEdges subgraph contain:" << std::endl |
780 | | << " " << subgraph->getDirectedEdges()->size() << " DirecteEdges " << std::endl |
781 | | << " " << subgraph->getNodes()->size() << " Nodes " << std::endl; |
782 | | #endif |
783 | 0 | processedGraphs.push_back(subgraph); |
784 | | #if GEOS_DEBUG |
785 | | std::cerr << " added " << subgraph << " to processedGraphs, new size is " |
786 | | << processedGraphs.size() << std::endl; |
787 | | #endif |
788 | 0 | polyBuilder.add(subgraph->getDirectedEdges(), subgraph->getNodes()); |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | /*private*/ |
793 | | std::unique_ptr<geom::Geometry> |
794 | | BufferBuilder::createEmptyResultGeometry() const |
795 | 0 | { |
796 | 0 | return geomFact->createPolygon(); |
797 | 0 | } |
798 | | |
799 | | } // namespace geos.operation.buffer |
800 | | } // namespace geos.operation |
801 | | } // namespace geos |
802 | | |