/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 |