Coverage Report

Created: 2025-11-08 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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