Coverage Report

Created: 2025-11-24 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/intersection/RectangleIntersection.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2014 Mika Heiskanen <mika.heiskanen@fmi.fi>
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************/
14
15
#include <geos/algorithm/PointLocation.h>
16
#include <geos/algorithm/Orientation.h>
17
#include <geos/operation/intersection/RectangleIntersection.h>
18
#include <geos/operation/intersection/Rectangle.h>
19
#include <geos/operation/intersection/RectangleIntersectionBuilder.h>
20
#include <geos/operation/overlayng/ElevationModel.h>
21
#include <geos/operation/predicate/RectangleIntersects.h>
22
#include <geos/geom/GeometryFactory.h>
23
#include <geos/geom/CoordinateSequence.h>
24
#include <geos/geom/Polygon.h>
25
#include <geos/geom/MultiPolygon.h>
26
#include <geos/geom/Point.h>
27
#include <geos/geom/Geometry.h>
28
#include <geos/geom/LineString.h>
29
#include <geos/geom/LinearRing.h>
30
#include <geos/geom/Location.h>
31
#include <geos/util/UnsupportedOperationException.h>
32
#include <geos/util.h>
33
#include <list>
34
#include <stdexcept>
35
36
using geos::operation::intersection::Rectangle;
37
using geos::operation::intersection::RectangleIntersectionBuilder;
38
using geos::operation::overlayng::ElevationModel;
39
using namespace geos::geom;
40
using namespace geos::algorithm;
41
namespace geos {
42
namespace operation { // geos::operation
43
namespace intersection { // geos::operation::intersection
44
45
/**
46
 * \brief Test if two coordinates are different
47
 */
48
49
inline
50
bool
51
different(double x1, double y1, double x2, double y2)
52
0
{
53
0
    return !(x1 == x2 && y1 == y2);
54
0
}
55
56
/**
57
 * \brief Calculate a line intersection point
58
 *
59
 * Note:
60
 *   - Calling this with x1,y1 and x2,y2 swapped cuts the other end of the line
61
 *   - Calling this with x and y swapped cuts in y-direction instead
62
 *   - Calling with 1<->2 and x<->y swapped works too
63
 */
64
65
inline
66
void
67
clip_one_edge(double& x1, double& y1, double& z1, double x2, double y2, double z2, double limit)
68
0
{
69
0
    if(x2 == limit) {
70
0
        y1 = y2;
71
0
        x1 = x2;
72
0
        z1 = z2;
73
0
    }
74
75
0
    if(x1 != x2) {
76
0
        double fraction = (limit - x1) / (x2 - x1);
77
0
        y1 += (y2 - y1) * fraction;
78
0
        z1 += (z2 - z1) * fraction;
79
0
        x1 = limit;
80
0
    }
81
0
}
82
83
/**
84
 * \brief Start point is outside, endpoint is definitely inside
85
 *
86
 * Note: Even though one might think using >= etc operators would produce
87
 *       the same result, that is not the case. We rely on the fact
88
 *       that nothing is clipped unless the point is truly outside the
89
 *       rectangle! Without this handling lines ending on the edges of
90
 *       the rectangle would be very difficult.
91
 */
92
93
void
94
clip_to_edges(double& x1, double& y1, double& z1,
95
              double x2, double y2, double z2,
96
              const Rectangle& rect)
97
0
{
98
0
    if(x1 < rect.xmin()) {
99
0
        clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmin());
100
0
    }
101
0
    else if(x1 > rect.xmax()) {
102
0
        clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmax());
103
0
    }
104
105
0
    if(y1 < rect.ymin()) {
106
0
        clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymin());
107
0
    }
108
0
    else if(y1 > rect.ymax()) {
109
0
        clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymax());
110
0
    }
111
0
}
112
113
114
/**
115
 * \brief Clip  geometry
116
 *
117
 * Here outGeom may also be a MultiPoint
118
 */
119
120
void
121
RectangleIntersection::clip_point(const geom::Point* g,
122
                                  RectangleIntersectionBuilder& parts,
123
                                  const Rectangle& rect)
124
0
{
125
0
    if(g == nullptr || g->isEmpty()) {
126
0
        return;
127
0
    }
128
129
0
    double x = g->getX();
130
0
    double y = g->getY();
131
132
0
    if(rect.position(x, y) == Rectangle::Inside) {
133
0
        parts.add(g->clone().release());
134
0
    }
135
0
}
136
137
bool
138
RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
139
        RectangleIntersectionBuilder& parts,
140
        const Rectangle& rect)
141
0
{
142
0
    auto n = gi->getNumPoints();
143
144
0
    if(gi == nullptr || n < 1) {
145
0
        return false;
146
0
    }
147
148
    // For shorthand code
149
150
0
    std::vector<Coordinate> cs;
151
0
    gi->getCoordinatesRO()->toVector(cs);
152
    //const geom::CoordinateSequence &cs = *(gi->getCoordinatesRO());
153
154
    // Keep a record of the point where a line segment entered
155
    // the rectangle. If the boolean is set, we must insert
156
    // the point to the beginning of the linestring which then
157
    // continues inside the rectangle.
158
159
0
    double x0 = 0;
160
0
    double y0 = 0;
161
0
    double z0 = 0;
162
0
    bool add_start = false;
163
164
    // Start iterating
165
166
0
    std::size_t i = 0;
167
168
0
    while(i < n) {
169
        // Establish initial position
170
171
0
        double x = cs[i].x;
172
0
        double y = cs[i].y;
173
0
        double z = cs[i].z;
174
0
        Rectangle::Position pos = rect.position(x, y);
175
176
0
        if(pos == Rectangle::Outside) {
177
            // Skip points as fast as possible until something has to be checked
178
            // in more detail.
179
180
0
            ++i;  // we already know it is outside
181
182
0
            if(x < rect.xmin())
183
0
                while(i < n && cs[i].x < rect.xmin()) {
184
0
                    ++i;
185
0
                }
186
187
0
            else if(x > rect.xmax())
188
0
                while(i < n && cs[i].x > rect.xmax()) {
189
0
                    ++i;
190
0
                }
191
192
0
            else if(y < rect.ymin())
193
0
                while(i < n && cs[i].y < rect.ymin()) {
194
0
                    ++i;
195
0
                }
196
197
0
            else if(y > rect.ymax())
198
0
                while(i < n && cs[i].y > rect.ymax()) {
199
0
                    ++i;
200
0
                }
201
202
0
            if(i >= n) {
203
0
                return false;
204
0
            }
205
206
            // Establish new position
207
0
            x = cs[i].x;
208
0
            y = cs[i].y;
209
0
            z = cs[i].z;
210
0
            pos = rect.position(x, y);
211
212
            // Handle all possible cases
213
0
            x0 = cs[i - 1].x;
214
0
            y0 = cs[i - 1].y;
215
0
            z0 = cs[i - 1].z;
216
0
            clip_to_edges(x0, y0, z0, x, y, z, rect);
217
218
0
            if(pos == Rectangle::Inside) {
219
0
                add_start = true; // x0,y0 must have clipped the rectangle
220
                // Main loop will enter the Inside/Edge section
221
222
0
            }
223
0
            else if(pos == Rectangle::Outside) {
224
                // From Outside to Outside. We need to check whether
225
                // we created a line segment inside the box. In any
226
                // case, we will continue the main loop after this
227
                // which will then enter the Outside section.
228
229
                // Clip the other end too
230
0
                clip_to_edges(x, y, z, x0, y0, z0, rect);
231
232
0
                Rectangle::Position prev_pos = rect.position(x0, y0);
233
0
                pos = rect.position(x, y);
234
235
0
                if(different(x0, y0, x, y) &&   // discard corners etc
236
0
                        Rectangle::onEdge(prev_pos) &&   // discard if does not intersect rect
237
0
                        Rectangle::onEdge(pos) &&
238
0
                        !Rectangle::onSameEdge(prev_pos, pos)  // discard if travels along edge
239
0
                  ) {
240
0
                    auto coords = detail::make_unique<geom::CoordinateSequence>(2u);
241
0
                    coords->setAt(Coordinate(x0, y0, z0), 0);
242
0
                    coords->setAt(Coordinate(x, y, z), 1);
243
0
                    auto line = _gf->createLineString(std::move(coords));
244
0
                    parts.add(line.release());
245
0
                }
246
247
                // Continue main loop outside the rect
248
249
0
            }
250
0
            else {
251
                // From outside to edge. If the edge we hit first when
252
                // following the line is not the edge we end at, then
253
                // clearly we must go through the rectangle and hence
254
                // a start point must be set.
255
256
0
                Rectangle::Position newpos = rect.position(x0, y0);
257
0
                if(!Rectangle::onSameEdge(pos, newpos)) {
258
0
                    add_start = true;
259
0
                }
260
0
                else {
261
                    // we ignore the travel along the edge and continue
262
                    // the main loop at the last edge point
263
0
                }
264
0
            }
265
0
        }
266
267
0
        else {
268
            // The point is now strictly inside or on the edge.
269
            // Keep iterating until the end or the point goes
270
            // outside. We may have to output partial linestrings
271
            // while iterating until we go strictly outside
272
273
0
            auto start_index = i;     // 1st valid original point
274
0
            bool go_outside = false;
275
276
0
            while(!go_outside && ++i < n) {
277
0
                x = cs[i].x;
278
0
                y = cs[i].y;
279
0
                z = cs[i].z;
280
281
0
                Rectangle::Position prev_pos = pos;
282
0
                pos = rect.position(x, y);
283
284
0
                if(pos == Rectangle::Inside) {
285
                    // Just keep going
286
0
                }
287
0
                else if(pos == Rectangle::Outside) {
288
0
                    go_outside = true;
289
290
                    // Clip the outside point to edges
291
0
                    clip_to_edges(x, y, z, cs[i - 1].x, cs[i - 1].y, cs[i - 1].z, rect);
292
0
                    pos = rect.position(x, y);
293
294
                    // Does the line exit through the inside of the box?
295
296
0
                    bool through_box = (different(x, y, cs[i].x, cs[i].y) &&
297
0
                                        !Rectangle::onSameEdge(prev_pos, pos));
298
299
                    // Output a LineString if it at least one segment long
300
301
0
                    if(start_index < i - 1 || add_start || through_box) {
302
0
                        auto coords = detail::make_unique<CoordinateSequence>();
303
0
                        if(add_start) {
304
0
                            coords->add(Coordinate(x0, y0, z0));
305
0
                            add_start = false;
306
0
                        }
307
                        //line->addSubLineString(&g, start_index, i-1);
308
0
                        coords->add(cs.begin() + static_cast<long>(start_index),
309
0
                                    cs.begin() + static_cast<long>(i));
310
311
0
                        if(through_box) {
312
0
                            coords->add(Coordinate(x, y, z));
313
0
                        }
314
315
0
                        auto line = _gf->createLineString(std::move(coords));
316
0
                        parts.add(line.release());
317
0
                    }
318
                    // And continue main loop on the outside
319
0
                }
320
0
                else {
321
                    // on same edge?
322
0
                    if(Rectangle::onSameEdge(prev_pos, pos)) {
323
                        // Nothing to output if we haven't been elsewhere
324
0
                        if(start_index < i - 1 || add_start) {
325
0
                        auto coords = detail::make_unique<CoordinateSequence>();
326
                            //geom::LineString * line = new geom::LineString();
327
0
                            if(add_start) {
328
                                //line->addPoint(x0,y0);
329
0
                                coords->add(Coordinate(x0, y0, z0));
330
0
                                add_start = false;
331
0
                            }
332
                            //line->addSubLineString(&g, start_index, i-1);
333
0
                            coords->add(cs.begin() + static_cast<long>(start_index),
334
0
                                        cs.begin() + static_cast<long>(i));
335
336
0
                            auto line = _gf->createLineString(std::move(coords));
337
0
                            parts.add(line.release());
338
0
                        }
339
0
                        start_index = i;
340
0
                    }
341
0
                    else {
342
                        // On different edge. Must have gone through the box
343
                        // then - keep collecting points that generate inside
344
                        // line segments
345
0
                    }
346
0
                }
347
0
            }
348
349
            // Was everything in? If so, generate no output but return true in this case only
350
0
            if(start_index == 0 && i >= n) {
351
0
                return true;
352
0
            }
353
354
            // Flush the last line segment if data ended and there is something to flush
355
356
0
            if(!go_outside &&           // meaning data ended
357
0
                    (start_index < i - 1 || add_start)) { // meaning something has to be generated
358
0
                auto coords = detail::make_unique<CoordinateSequence>();
359
                //geom::LineString * line = new geom::LineString();
360
0
                if(add_start) {
361
                    //line->addPoint(x0,y0);
362
0
                    coords->add(Coordinate(x0, y0, z0));
363
0
                    add_start = false;
364
0
                }
365
                //line->addSubLineString(&g, start_index, i-1);
366
0
                coords->add(cs.begin() + static_cast<long>(start_index),
367
0
                            cs.begin() + static_cast<long>(i));
368
369
0
                auto line = _gf->createLineString(std::move(coords));
370
0
                parts.add(line.release());
371
0
            }
372
373
0
        }
374
0
    }
375
376
0
    return false;
377
378
0
}
379
380
/**
381
 * \brief Clip polygon, do not close clipped ones
382
 */
383
384
void
385
RectangleIntersection::clip_polygon_to_linestrings(const geom::Polygon* g,
386
        RectangleIntersectionBuilder& toParts,
387
        const Rectangle& rect)
388
0
{
389
0
    if(g == nullptr || g->isEmpty()) {
390
0
        return;
391
0
    }
392
393
    // Clip the exterior first to see what's going on
394
395
0
    RectangleIntersectionBuilder parts(*_gf);
396
397
    // If everything was in, just clone the original
398
399
0
    if(clip_linestring_parts(g->getExteriorRing(), parts, rect)) {
400
0
        toParts.add(g->clone().release());
401
0
        return;
402
0
    }
403
404
    // Now, if parts is empty, our rectangle may be inside the polygon
405
    // If not, holes are outside too
406
407
0
    if(parts.empty()) {
408
        // We could now check whether the rectangle is inside the outer
409
        // ring to avoid checking the holes. However, if holes are much
410
        // smaller than the exterior ring just checking the holes
411
        // separately could be faster.
412
413
0
        if(g->getNumInteriorRing() == 0) {
414
0
            return;
415
0
        }
416
417
0
    }
418
0
    else {
419
        // The exterior must have been clipped into linestrings.
420
        // Move them to the actual parts collector, clearing parts.
421
422
0
        parts.reconnect();
423
0
        parts.release(toParts);
424
0
    }
425
426
    // Handle the holes now:
427
    // - Clipped ones become linestrings
428
    // - Intact ones become new polygons without holes
429
430
0
    for(std::size_t i = 0, n = g->getNumInteriorRing(); i < n; ++i) {
431
0
        if(clip_linestring_parts(g->getInteriorRingN(i), parts, rect)) {
432
            // clones
433
0
            auto hole =g->getInteriorRingN(i)->clone();
434
            // becomes exterior
435
0
            Polygon* poly = _gf->createPolygon(std::move(hole)).release();
436
0
            toParts.add(poly);
437
0
        }
438
0
        else if(!parts.empty()) {
439
0
            parts.reconnect();
440
0
            parts.release(toParts);
441
0
        }
442
0
    }
443
0
}
444
445
/**
446
 * \brief Clip polygon, close clipped ones
447
 */
448
449
void
450
RectangleIntersection::clip_polygon_to_polygons(const geom::Polygon* g,
451
        RectangleIntersectionBuilder& toParts,
452
        const Rectangle& rect)
453
0
{
454
0
    if(g == nullptr || g->isEmpty()) {
455
0
        return;
456
0
    }
457
458
    // Clip the exterior first to see what's going on
459
460
0
    RectangleIntersectionBuilder parts(*_gf);
461
462
    // If everything was in, just clone the original
463
464
0
    const LineString* shell = g->getExteriorRing();
465
0
    if(clip_linestring_parts(shell, parts, rect)) {
466
0
        toParts.add(g->clone().release());
467
0
        return;
468
0
    }
469
470
    // If there were no intersections, the outer ring might be
471
    // completely outside.
472
473
0
    using geos::algorithm::Orientation;
474
0
    if(parts.empty()) {
475
0
        Coordinate rectCenter(rect.xmin(), rect.ymin());
476
0
        rectCenter.x += (rect.xmax() - rect.xmin()) / 2;
477
0
        rectCenter.y += (rect.ymax() - rect.ymin()) / 2;
478
0
        if(PointLocation::locateInRing(rectCenter,
479
0
                                       *g->getExteriorRing()->getCoordinatesRO())
480
0
                != Location::INTERIOR) {
481
0
            return;
482
0
        }
483
0
    }
484
0
    else {
485
        // TODO: make CCW checking part of clip_linestring_parts ?
486
0
        if(Orientation::isCCW(shell->getCoordinatesRO())) {
487
0
            parts.reverseLines();
488
0
        }
489
0
    }
490
491
    // Must do this to make sure all end points are on the edges
492
493
0
    parts.reconnect();
494
495
    // Handle the holes now:
496
    // - Clipped ones become part of the exterior
497
    // - Intact ones become holes in new polygons formed by exterior parts
498
499
500
0
    for(std::size_t i = 0, n = g->getNumInteriorRing(); i < n; ++i) {
501
0
        RectangleIntersectionBuilder holeparts(*_gf);
502
0
        const LinearRing* hole = g->getInteriorRingN(i);
503
0
        if(clip_linestring_parts(hole, holeparts, rect)) {
504
            // becomes exterior
505
0
            auto cloned = hole->clone();
506
0
            Polygon* poly = _gf->createPolygon(std::move(cloned)).release();
507
0
            parts.add(poly);
508
0
        }
509
0
        else {
510
0
            if(!holeparts.empty()) {
511
                // TODO: make CCW checking part of clip_linestring_parts ?
512
0
                if(! Orientation::isCCW(hole->getCoordinatesRO())) {
513
0
                    holeparts.reverseLines();
514
0
                }
515
0
                holeparts.reconnect();
516
0
                holeparts.release(parts);
517
0
            }
518
0
            else {
519
520
0
                Coordinate rectCenter(rect.xmin(), rect.ymin());
521
0
                rectCenter.x += (rect.xmax() - rect.xmin()) / 2;
522
0
                rectCenter.y += (rect.ymax() - rect.ymin()) / 2;
523
0
                if(PointLocation::isInRing(rectCenter,
524
0
                                           g->getInteriorRingN(i)->getCoordinatesRO())) {
525
                    // Completely inside the hole
526
0
                    return;
527
0
                }
528
0
            }
529
0
        }
530
0
    }
531
532
0
    parts.reconnectPolygons(rect);
533
0
    parts.release(toParts);
534
0
}
535
536
/**
537
 * \brief Clip  geometry
538
 */
539
540
void
541
RectangleIntersection::clip_polygon(const geom::Polygon* g,
542
                                    RectangleIntersectionBuilder& parts,
543
                                    const Rectangle& rect,
544
                                    bool keep_polygons)
545
0
{
546
0
    if(g == nullptr || g->isEmpty()) {
547
0
        return;
548
0
    }
549
550
0
    if(keep_polygons) {
551
0
        clip_polygon_to_polygons(g, parts, rect);
552
0
    }
553
0
    else {
554
0
        clip_polygon_to_linestrings(g, parts, rect);
555
0
    }
556
0
}
557
558
/**
559
 * \brief Clip geometry
560
 */
561
562
void
563
RectangleIntersection::clip_linestring(const geom::LineString* g,
564
                                       RectangleIntersectionBuilder& parts,
565
                                       const Rectangle& rect)
566
0
{
567
0
    if(g == nullptr || g->isEmpty()) {
568
0
        return;
569
0
    }
570
571
    // If everything was in, just clone the original
572
573
0
    if(clip_linestring_parts(g, parts, rect)) {
574
0
        parts.add(g->clone().release());
575
0
    }
576
577
0
}
578
579
void
580
RectangleIntersection::clip_multipoint(const geom::MultiPoint* g,
581
                                       RectangleIntersectionBuilder& parts,
582
                                       const Rectangle& rect)
583
0
{
584
0
    if(g == nullptr || g->isEmpty()) {
585
0
        return;
586
0
    }
587
0
    for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) {
588
0
        clip_point(g->getGeometryN(i), parts, rect);
589
0
    }
590
0
}
591
592
void
593
RectangleIntersection::clip_multilinestring(const geom::MultiLineString* g,
594
        RectangleIntersectionBuilder& parts,
595
        const Rectangle& rect)
596
0
{
597
0
    if(g == nullptr || g->isEmpty()) {
598
0
        return;
599
0
    }
600
601
0
    for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) {
602
0
        clip_linestring(g->getGeometryN(i), parts, rect);
603
0
    }
604
0
}
605
606
void
607
RectangleIntersection::clip_multipolygon(const geom::MultiPolygon* g,
608
        RectangleIntersectionBuilder& parts,
609
        const Rectangle& rect,
610
        bool keep_polygons)
611
0
{
612
0
    if(g == nullptr || g->isEmpty()) {
613
0
        return;
614
0
    }
615
616
0
    for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) {
617
0
        clip_polygon(g->getGeometryN(i), parts, rect, keep_polygons);
618
0
    }
619
0
}
620
621
void
622
RectangleIntersection::clip_geometrycollection(
623
    const geom::GeometryCollection* g,
624
    RectangleIntersectionBuilder& parts,
625
    const Rectangle& rect,
626
    bool keep_polygons)
627
0
{
628
0
    if(g == nullptr || g->isEmpty()) {
629
0
        return;
630
0
    }
631
632
0
    for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) {
633
0
        clip_geom(g->getGeometryN(i),
634
0
                  parts, rect, keep_polygons);
635
0
    }
636
0
}
637
638
void
639
RectangleIntersection::clip_geom(const geom::Geometry* g,
640
                                 RectangleIntersectionBuilder& parts,
641
                                 const Rectangle& rect,
642
                                 bool keep_polygons)
643
0
{
644
0
    if(const Point* p1 = dynamic_cast<const geom::Point*>(g)) {
645
0
        return clip_point(p1, parts, rect);
646
0
    }
647
0
    else if(const MultiPoint* p2 = dynamic_cast<const geom::MultiPoint*>(g)) {
648
0
        return clip_multipoint(p2, parts, rect);
649
0
    }
650
0
    else if(const LineString* p3 = dynamic_cast<const geom::LineString*>(g)) {
651
0
        return clip_linestring(p3, parts, rect);
652
0
    }
653
0
    else if(const MultiLineString* p4 = dynamic_cast<const geom::MultiLineString*>(g)) {
654
0
        return clip_multilinestring(p4, parts, rect);
655
0
    }
656
0
    else if(const Polygon* p5 = dynamic_cast<const geom::Polygon*>(g)) {
657
0
        return clip_polygon(p5, parts, rect, keep_polygons);
658
0
    }
659
0
    else if(const MultiPolygon* p6 = dynamic_cast<const geom::MultiPolygon*>(g)) {
660
0
        return clip_multipolygon(p6, parts, rect, keep_polygons);
661
0
    }
662
0
    else if(const GeometryCollection* p7 = dynamic_cast<const geom::GeometryCollection*>(g)) {
663
0
        return clip_geometrycollection(p7, parts, rect, keep_polygons);
664
0
    }
665
0
    else {
666
0
        throw util::UnsupportedOperationException("Encountered an unknown geometry component when clipping polygons");
667
0
    }
668
0
}
669
670
/* public static */
671
std::unique_ptr<geom::Geometry>
672
RectangleIntersection::clipBoundary(const geom::Geometry& g, const Rectangle& rect)
673
0
{
674
0
    RectangleIntersection ri(g, rect);
675
0
    return ri.clipBoundary();
676
0
}
677
678
std::unique_ptr<geom::Geometry>
679
RectangleIntersection::clipBoundary()
680
0
{
681
0
    RectangleIntersectionBuilder parts(*_gf);
682
683
0
    bool keep_polygons = false;
684
0
    clip_geom(&_geom, parts, _rect, keep_polygons);
685
686
0
    return parts.build();
687
0
}
688
689
/* public static */
690
std::unique_ptr<geom::Geometry>
691
RectangleIntersection::clip(const geom::Geometry& g, const Rectangle& rect)
692
0
{
693
0
    RectangleIntersection ri(g, rect);
694
0
    std::unique_ptr<geom::Geometry> result = ri.clip();
695
696
0
    if (g.hasZ()) {
697
0
        std::unique_ptr<ElevationModel> elevModel = ElevationModel::create(g);
698
0
        elevModel->populateZ(*result);
699
0
    }
700
701
0
    return result;
702
0
}
703
704
std::unique_ptr<geom::Geometry>
705
RectangleIntersection::clip()
706
0
{
707
0
    RectangleIntersectionBuilder parts(*_gf);
708
709
0
    bool keep_polygons = true;
710
0
    clip_geom(&_geom, parts, _rect, keep_polygons);
711
712
0
    return parts.build();
713
0
}
714
715
RectangleIntersection::RectangleIntersection(const geom::Geometry& geom, const Rectangle& rect)
716
0
    : _geom(geom), _rect(rect),
717
0
      _gf(geom.getFactory())
718
0
{
719
0
}
720
721
} // namespace geos::operation::intersection
722
} // namespace geos::operation
723
} // namespace geos