Coverage Report

Created: 2022-08-24 06:40

/src/geos/src/geom/Polygon.cpp
Line
Count
Source (jump to first uncovered line)
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: geom/Polygon.java r320 (JTS-1.12)
18
 *
19
 **********************************************************************/
20
21
#include <geos/algorithm/Area.h>
22
#include <geos/algorithm/Orientation.h>
23
#include <geos/util.h>
24
#include <geos/geom/Coordinate.h>
25
#include <geos/geom/Polygon.h>
26
#include <geos/geom/LinearRing.h>
27
#include <geos/geom/MultiLineString.h> // for getBoundary()
28
#include <geos/geom/GeometryFactory.h>
29
#include <geos/geom/Dimension.h>
30
#include <geos/geom/Envelope.h>
31
#include <geos/geom/CoordinateSequenceFactory.h>
32
#include <geos/geom/CoordinateArraySequence.h>
33
#include <geos/geom/CoordinateSequenceFilter.h>
34
#include <geos/geom/GeometryFilter.h>
35
#include <geos/geom/GeometryComponentFilter.h>
36
37
#include <vector>
38
#include <cmath> // for fabs
39
#include <cassert>
40
#include <algorithm>
41
#include <memory>
42
43
#ifndef GEOS_DEBUG
44
#define GEOS_DEBUG 0
45
#endif
46
47
//using namespace geos::algorithm;
48
49
namespace geos {
50
namespace geom { // geos::geom
51
52
/*protected*/
53
Polygon::Polygon(const Polygon& p)
54
    :
55
    Geometry(p),
56
    shell(detail::make_unique<LinearRing>(*p.shell)),
57
    holes(p.holes.size())
58
160k
{
59
344k
    for(std::size_t i = 0; i < holes.size(); ++i) {
60
183k
        holes[i] = detail::make_unique<LinearRing>(*p.holes[i]);
61
183k
    }
62
160k
}
63
64
/*protected*/
65
Polygon::Polygon(LinearRing* newShell, std::vector<LinearRing*>* newHoles,
66
                 const GeometryFactory* newFactory):
67
    Geometry(newFactory)
68
171k
{
69
171k
    if(newShell == nullptr) {
70
19
        shell = getFactory()->createLinearRing();
71
19
    }
72
171k
    else {
73
171k
        if(newHoles != nullptr && newShell->isEmpty() && hasNonEmptyElements(newHoles)) {
74
0
            throw util::IllegalArgumentException("shell is empty but holes are not");
75
0
        }
76
171k
        shell.reset(newShell);
77
171k
    }
78
79
171k
    if(newHoles != nullptr) {
80
115k
        if(hasNullElements(newHoles)) {
81
0
            throw util::IllegalArgumentException("holes must not contain null elements");
82
0
        }
83
115k
        for (const auto& hole : *newHoles) {
84
7.27k
            holes.emplace_back(hole);
85
7.27k
        }
86
115k
        delete newHoles;
87
115k
    }
88
171k
}
89
90
Polygon::Polygon(std::unique_ptr<LinearRing> && newShell,
91
                 const GeometryFactory& newFactory) :
92
        Geometry(&newFactory),
93
        shell(std::move(newShell))
94
2.16k
{
95
2.16k
    if(shell == nullptr) {
96
0
        shell = getFactory()->createLinearRing();
97
0
    }
98
2.16k
}
99
100
Polygon::Polygon(std::unique_ptr<LinearRing> && newShell,
101
                 std::vector<std::unique_ptr<LinearRing>> && newHoles,
102
                 const GeometryFactory& newFactory) :
103
                 Geometry(&newFactory),
104
                 shell(std::move(newShell)),
105
                 holes(std::move(newHoles))
106
53.0k
{
107
53.0k
    if(shell == nullptr) {
108
0
        shell = getFactory()->createLinearRing();
109
0
    }
110
111
    // TODO move into validateConstruction() method
112
53.0k
    if(shell->isEmpty() && hasNonEmptyElements(&holes)) {
113
2
        throw util::IllegalArgumentException("shell is empty but holes are not");
114
2
    }
115
53.0k
    if (hasNullElements(&holes)) {
116
0
        throw util::IllegalArgumentException("holes must not contain null elements");
117
0
    }
118
53.0k
}
119
120
121
std::unique_ptr<CoordinateSequence>
122
Polygon::getCoordinates() const
123
0
{
124
0
    if(isEmpty()) {
125
0
        return getFactory()->getCoordinateSequenceFactory()->create();
126
0
    }
127
128
0
    std::vector<Coordinate> cl;
129
0
    cl.reserve(getNumPoints());
130
131
    // Add shell points
132
0
    const CoordinateSequence* shellCoords = shell->getCoordinatesRO();
133
0
    shellCoords->toVector(cl);
134
135
    // Add holes points
136
0
    for(const auto& hole : holes) {
137
0
        const CoordinateSequence* childCoords = hole->getCoordinatesRO();
138
0
        childCoords->toVector(cl);
139
0
    }
140
141
0
    return getFactory()->getCoordinateSequenceFactory()->create(std::move(cl));
142
0
}
143
144
size_t
145
Polygon::getNumPoints() const
146
0
{
147
0
    std::size_t numPoints = shell->getNumPoints();
148
0
    for(const auto& lr : holes) {
149
0
        numPoints += lr->getNumPoints();
150
0
    }
151
0
    return numPoints;
152
0
}
153
154
Dimension::DimensionType
155
Polygon::getDimension() const
156
393k
{
157
393k
    return Dimension::A; // area
158
393k
}
159
160
uint8_t
161
Polygon::getCoordinateDimension() const
162
95.2k
{
163
95.2k
    uint8_t dimension = 2;
164
165
95.2k
    if(shell != nullptr) {
166
95.2k
        dimension = std::max(dimension, shell->getCoordinateDimension());
167
95.2k
    }
168
169
95.2k
    for(const auto& hole : holes) {
170
45.6k
        dimension = std::max(dimension, hole->getCoordinateDimension());
171
45.6k
    }
172
173
95.2k
    return dimension;
174
95.2k
}
175
176
int
177
Polygon::getBoundaryDimension() const
178
0
{
179
0
    return 1;
180
0
}
181
182
bool
183
Polygon::isEmpty() const
184
1.18M
{
185
1.18M
    return shell->isEmpty();
186
1.18M
}
187
188
const LinearRing*
189
Polygon::getExteriorRing() const
190
545k
{
191
545k
    return shell.get();
192
545k
}
193
194
std::unique_ptr<LinearRing>
195
Polygon::releaseExteriorRing()
196
0
{
197
0
    envelope.reset();
198
0
    return std::move(shell);
199
0
}
200
201
size_t
202
Polygon::getNumInteriorRing() const
203
2.43M
{
204
2.43M
    return holes.size();
205
2.43M
}
206
207
const LinearRing*
208
Polygon::getInteriorRingN(std::size_t n) const
209
2.03M
{
210
2.03M
    return holes[n].get();
211
2.03M
}
212
213
std::vector<std::unique_ptr<LinearRing>>
214
Polygon::releaseInteriorRings()
215
0
{
216
0
    return std::move(holes);
217
0
}
218
219
std::string
220
Polygon::getGeometryType() const
221
0
{
222
0
    return "Polygon";
223
0
}
224
225
// Returns a newly allocated Geometry object
226
/*public*/
227
std::unique_ptr<Geometry>
228
Polygon::getBoundary() const
229
0
{
230
    /*
231
     * We will make sure that what we
232
     * return is composed of LineString,
233
     * not LinearRings
234
     */
235
236
0
    const GeometryFactory* gf = getFactory();
237
238
0
    if(isEmpty()) {
239
0
        return std::unique_ptr<Geometry>(gf->createMultiLineString());
240
0
    }
241
242
0
    if(holes.empty()) {
243
0
        return std::unique_ptr<Geometry>(gf->createLineString(*shell));
244
0
    }
245
246
0
    std::vector<std::unique_ptr<Geometry>> rings(holes.size() + 1);
247
248
0
    rings[0] = gf->createLineString(*shell);
249
0
    for(std::size_t i = 0, n = holes.size(); i < n; ++i) {
250
0
        const LinearRing* hole = holes[i].get();
251
0
        std::unique_ptr<LineString> ls = gf->createLineString(*hole);
252
0
        rings[i + 1] = std::move(ls);
253
0
    }
254
255
0
    return getFactory()->createMultiLineString(std::move(rings));
256
0
}
257
258
Envelope::Ptr
259
Polygon::computeEnvelopeInternal() const
260
57.9k
{
261
57.9k
    return detail::make_unique<Envelope>(*(shell->getEnvelopeInternal()));
262
57.9k
}
263
264
bool
265
Polygon::equalsExact(const Geometry* other, double tolerance) const
266
0
{
267
0
    if(!isEquivalentClass(other)) {
268
0
        return false;
269
0
    }
270
271
0
    const Polygon* otherPolygon = detail::down_cast<const Polygon*>(other);
272
0
    if(! otherPolygon) {
273
0
        return false;
274
0
    }
275
276
0
    if(!shell->equalsExact(otherPolygon->shell.get(), tolerance)) {
277
0
        return false;
278
0
    }
279
280
0
    std::size_t nholes = holes.size();
281
282
0
    if(nholes != otherPolygon->holes.size()) {
283
0
        return false;
284
0
    }
285
286
0
    for(std::size_t i = 0; i < nholes; i++) {
287
0
        const LinearRing* hole = holes[i].get();
288
0
        const LinearRing* otherhole = otherPolygon->holes[i].get();
289
0
        if(!hole->equalsExact(otherhole, tolerance)) {
290
0
            return false;
291
0
        }
292
0
    }
293
294
0
    return true;
295
0
}
296
297
void
298
Polygon::apply_ro(CoordinateFilter* filter) const
299
110k
{
300
110k
    shell->apply_ro(filter);
301
110k
    for(const auto& lr : holes) {
302
69.3k
        lr->apply_ro(filter);
303
69.3k
    }
304
110k
}
305
306
void
307
Polygon::apply_rw(const CoordinateFilter* filter)
308
12.6k
{
309
12.6k
    shell->apply_rw(filter);
310
12.6k
    for(auto& lr : holes) {
311
9.99k
        lr->apply_rw(filter);
312
9.99k
    }
313
12.6k
}
314
315
void
316
Polygon::apply_rw(GeometryFilter* filter)
317
0
{
318
0
    filter->filter_rw(this);
319
0
}
320
321
void
322
Polygon::apply_ro(GeometryFilter* filter) const
323
0
{
324
0
    filter->filter_ro(this);
325
0
}
326
327
std::unique_ptr<Geometry>
328
Polygon::convexHull() const
329
0
{
330
0
    return getExteriorRing()->convexHull();
331
0
}
332
333
334
void
335
Polygon::normalize()
336
0
{
337
0
    normalize(shell.get(), true);
338
0
    for(auto& lr : holes) {
339
0
        normalize(lr.get(), false);
340
0
    }
341
0
    std::sort(holes.begin(), holes.end(), [](const std::unique_ptr<LinearRing> & a, const std::unique_ptr<LinearRing> & b) {
342
0
        return a->compareTo(b.get()) > 0;
343
0
    });
344
0
}
345
346
int
347
Polygon::compareToSameClass(const Geometry* g) const
348
0
{
349
0
    const Polygon* p = detail::down_cast<const Polygon*>(g);
350
0
    int shellComp = shell->compareToSameClass(p->shell.get());
351
0
    if (shellComp != 0) {
352
0
        return shellComp;
353
0
    }
354
355
0
    size_t nHole1 = getNumInteriorRing();
356
0
    size_t nHole2 = p->getNumInteriorRing();
357
0
    if (nHole1 < nHole2) {
358
0
        return -1;
359
0
    }
360
0
    if (nHole1 > nHole2) {
361
0
        return 1;
362
0
    }
363
364
0
    for (size_t i=0; i < nHole1; i++) {
365
0
        const LinearRing *lr = p->getInteriorRingN(i);
366
0
        const int holeComp = getInteriorRingN(i)->compareToSameClass(lr);
367
0
        if (holeComp != 0) {
368
0
            return holeComp;
369
0
        }
370
0
    }
371
372
0
    return 0;
373
0
}
374
375
/*
376
 * TODO: check this function, there should be CoordinateSequence copy
377
 *       reduction possibility.
378
 */
379
void
380
Polygon::normalize(LinearRing* ring, bool clockwise)
381
0
{
382
0
    if(ring->isEmpty()) {
383
0
        return;
384
0
    }
385
386
0
    auto coords = detail::make_unique<std::vector<Coordinate>>();
387
0
    ring->getCoordinatesRO()->toVector(*coords);
388
0
    coords->erase(coords->end() - 1); // remove last point (repeated)
389
390
0
    auto uniqueCoordinates = detail::make_unique<CoordinateArraySequence>(coords.release());
391
392
0
    const Coordinate* minCoordinate = uniqueCoordinates->minCoordinate();
393
394
0
    CoordinateSequence::scroll(uniqueCoordinates.get(), minCoordinate);
395
0
    uniqueCoordinates->add(uniqueCoordinates->getAt(0));
396
0
    if(algorithm::Orientation::isCCW(uniqueCoordinates.get()) == clockwise) {
397
0
        CoordinateSequence::reverse(uniqueCoordinates.get());
398
0
    }
399
0
    ring->setPoints(uniqueCoordinates.get());
400
0
}
401
402
const Coordinate*
403
Polygon::getCoordinate() const
404
0
{
405
0
    return shell->getCoordinate();
406
0
}
407
408
/*
409
 *  Returns the area of this <code>Polygon</code>
410
 *
411
 * @return the area of the polygon
412
 */
413
double
414
Polygon::getArea() const
415
10.7k
{
416
10.7k
    double area = 0.0;
417
10.7k
    area += algorithm::Area::ofRing(shell->getCoordinatesRO());
418
10.7k
    for(const auto& lr : holes) {
419
9.44k
        const CoordinateSequence* h = lr->getCoordinatesRO();
420
9.44k
        area -= algorithm::Area::ofRing(h);
421
9.44k
    }
422
10.7k
    return area;
423
10.7k
}
424
425
/**
426
 * Returns the perimeter of this <code>Polygon</code>
427
 *
428
 * @return the perimeter of the polygon
429
 */
430
double
431
Polygon::getLength() const
432
0
{
433
0
    double len = 0.0;
434
0
    len += shell->getLength();
435
0
    for(const auto& hole : holes) {
436
0
        len += hole->getLength();
437
0
    }
438
0
    return len;
439
0
}
440
441
void
442
Polygon::apply_ro(GeometryComponentFilter* filter) const
443
6.89k
{
444
6.89k
    filter->filter_ro(this);
445
6.89k
    shell->apply_ro(filter);
446
147k
    for(std::size_t i = 0, n = holes.size(); i < n && !filter->isDone(); ++i) {
447
140k
        holes[i]->apply_ro(filter);
448
140k
    }
449
6.89k
}
450
451
void
452
Polygon::apply_rw(GeometryComponentFilter* filter)
453
9.72k
{
454
9.72k
    filter->filter_rw(this);
455
9.72k
    shell->apply_rw(filter);
456
15.9k
    for(std::size_t i = 0, n = holes.size(); i < n && !filter->isDone(); ++i) {
457
6.17k
        holes[i]->apply_rw(filter);
458
6.17k
    }
459
9.72k
}
460
461
void
462
Polygon::apply_rw(CoordinateSequenceFilter& filter)
463
0
{
464
0
    shell->apply_rw(filter);
465
466
0
    if(! filter.isDone()) {
467
0
        for(std::size_t i = 0, n = holes.size(); i < n; ++i) {
468
0
            holes[i]->apply_rw(filter);
469
0
            if(filter.isDone()) {
470
0
                break;
471
0
            }
472
0
        }
473
0
    }
474
0
    if(filter.isGeometryChanged()) {
475
0
        geometryChanged();
476
0
    }
477
0
}
478
479
void
480
Polygon::apply_ro(CoordinateSequenceFilter& filter) const
481
66.3k
{
482
66.3k
    shell->apply_ro(filter);
483
484
66.3k
    if(! filter.isDone()) {
485
1.38M
        for(std::size_t i = 0, n = holes.size(); i < n; ++i) {
486
1.37M
            holes[i]->apply_ro(filter);
487
1.37M
            if(filter.isDone()) {
488
934
                break;
489
934
            }
490
1.37M
        }
491
4.91k
    }
492
    //if (filter.isGeometryChanged()) geometryChanged();
493
66.3k
}
494
495
GeometryTypeId
496
Polygon::getGeometryTypeId() const
497
213k
{
498
213k
    return GEOS_POLYGON;
499
213k
}
500
501
bool
502
Polygon::isRectangle() const
503
0
{
504
0
    if(getNumInteriorRing() != 0) {
505
0
        return false;
506
0
    }
507
0
    assert(shell != nullptr);
508
0
    if(shell->getNumPoints() != 5) {
509
0
        return false;
510
0
    }
511
512
0
    const CoordinateSequence& seq = *(shell->getCoordinatesRO());
513
514
    // check vertices have correct values
515
0
    const Envelope& env = *getEnvelopeInternal();
516
0
    for(uint32_t i = 0; i < 5; i++) {
517
0
        double x = seq.getX(i);
518
0
        if(!(x == env.getMinX() || x == env.getMaxX())) {
519
0
            return false;
520
0
        }
521
0
        double y = seq.getY(i);
522
0
        if(!(y == env.getMinY() || y == env.getMaxY())) {
523
0
            return false;
524
0
        }
525
0
    }
526
527
    // check vertices are in right order
528
0
    double prevX = seq.getX(0);
529
0
    double prevY = seq.getY(0);
530
0
    for(uint32_t i = 1; i <= 4; i++) {
531
0
        double x = seq.getX(i);
532
0
        double y = seq.getY(i);
533
0
        bool xChanged = (x != prevX);
534
0
        bool yChanged = (y != prevY);
535
0
        if(xChanged == yChanged) {
536
0
            return false;
537
0
        }
538
0
        prevX = x;
539
0
        prevY = y;
540
0
    }
541
0
    return true;
542
0
}
543
544
Polygon*
545
Polygon::reverseImpl() const
546
0
{
547
0
    if(isEmpty()) {
548
0
        return clone().release();
549
0
    }
550
551
0
    std::vector<std::unique_ptr<LinearRing>> interiorRingsReversed(holes.size());
552
553
0
    std::transform(holes.begin(),
554
0
                   holes.end(),
555
0
                   interiorRingsReversed.begin(),
556
0
    [](const std::unique_ptr<LinearRing> & g) {
557
0
        return g->reverse();
558
0
    });
559
560
0
    return getFactory()->createPolygon(shell->reverse(), std::move(interiorRingsReversed)).release();
561
0
}
562
563
} // namespace geos::geom
564
} // namespace geos