OGS
Polygon.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "Polygon.h"
5
6#include <algorithm>
7
9#include "BaseLib/quicksort.h"
10
11namespace GeoLib
12{
23
33
45 MathLib::Point3d const& destination,
46 MathLib::Point3d const& pnt)
47{
48 long double const a[2] = {destination[0] - source[0],
49 destination[1] - source[1]}; // vector
50 long double const b[2] = {pnt[0] - source[0],
51 pnt[1] - source[1]}; // vector
52
53 long double const det_2x2(a[0] * b[1] - a[1] * b[0]);
54 constexpr double eps = std::numeric_limits<double>::epsilon();
55
56 if (det_2x2 > eps)
57 {
58 return Location::LEFT;
59 }
60 if (eps < std::abs(det_2x2))
61 {
62 return Location::RIGHT;
63 }
64 if (a[0] * b[0] < 0.0 || a[1] * b[1] < 0.0)
65 {
66 return Location::BEHIND;
67 }
68 if (a[0] * a[0] + a[1] * a[1] < b[0] * b[0] + b[1] * b[1])
69 {
70 return Location::BEYOND;
71 }
72 if (MathLib::sqrDist(pnt, source) < pow(eps, 2))
73 {
74 return Location::SOURCE;
75 }
76 if (MathLib::sqrDist(pnt, destination) < std::sqrt(eps))
77 {
79 }
80 return Location::BETWEEN;
81}
82
92 MathLib::Point3d const& b,
93 MathLib::Point3d const& pnt)
94{
95 switch (getLocationOfPoint(a, b, pnt))
96 {
97 case Location::LEFT:
98 {
99 if (a[1] < pnt[1] && pnt[1] <= b[1])
100 {
101 return EdgeType::CROSSING;
102 }
103
105 }
106 case Location::RIGHT:
107 {
108 if (b[1] < pnt[1] && pnt[1] <= a[1])
109 {
110 return EdgeType::CROSSING;
111 }
112
114 }
116 case Location::SOURCE:
118 return EdgeType::TOUCHING;
119 default:
121 }
122}
123
124Polygon::Polygon(const Polyline& ply, bool init)
125 : Polyline(ply), _aabb(ply.getPointsVec(), ply.getPolylinePointIDs())
126{
127 if (init)
128 {
129 initialise();
130 }
131 _simple_polygon_list.push_back(this);
132}
133
134Polygon::Polygon(Polygon const& other) : Polyline(other), _aabb(other._aabb)
135{
136 _simple_polygon_list.push_back(this);
137 auto sub_polygon_it(other._simple_polygon_list.begin());
138 for (sub_polygon_it++; // the first entry is the polygon itself, skip the
139 // entry
140 sub_polygon_it != other._simple_polygon_list.end();
141 ++sub_polygon_it)
142 {
143 _simple_polygon_list.emplace_back(new Polygon(*(*sub_polygon_it)));
144 }
145}
146
148{
149 // remove polygons from list
150 for (auto& polygon : _simple_polygon_list)
151 {
152 // the first entry of the list can be a pointer the object itself
153 if (polygon != this)
154 {
155 delete polygon;
156 }
157 }
158}
159
161{
162 if (this->isClosed())
163 {
165 return true;
166 }
167 WARN("Polygon::initialise(): base polyline is not closed.");
168 return false;
169}
170
178std::vector<GeoLib::Point> getAllIntersectionPoints(
179 Polygon const& polygon, GeoLib::LineSegment const& segment)
180{
181 std::vector<GeoLib::Point> intersections;
183 for (auto&& seg_it : polygon)
184 {
185 if (GeoLib::lineSegmentIntersect(seg_it, segment, s))
186 {
187 intersections.push_back(s);
188 }
189 }
190
191 return intersections;
192}
193
195{
196 auto const [min_aabb_pnt, max_aabb_pnt] = _aabb.getMinMaxPoints();
197
198 if (pnt[0] < min_aabb_pnt[0] || max_aabb_pnt[0] < pnt[0] ||
199 pnt[1] < min_aabb_pnt[1] || max_aabb_pnt[1] < pnt[1])
200 {
201 return false;
202 }
203
204 if (_simple_polygon_list.size() == 1)
205 {
206 std::size_t n_intersections(0);
207 const std::size_t n_nodes(getNumberOfPoints() - 1);
208 for (std::size_t k(0); k < n_nodes; k++)
209 {
210 if (((*(getPoint(k)))[1] <= pnt[1] &&
211 pnt[1] <= (*(getPoint(k + 1)))[1]) ||
212 ((*(getPoint(k + 1)))[1] <= pnt[1] &&
213 pnt[1] <= (*(getPoint(k)))[1]))
214 {
215 switch (getEdgeType(*getPoint(k), *getPoint(k + 1), pnt))
216 {
218 return true;
220 n_intersections++;
221 break;
223 break;
224 default:
225 // do nothing
226 ;
227 }
228 }
229 }
230 if (n_intersections % 2 == 1)
231 {
232 return true;
233 }
234 }
235 else
236 {
237 for (auto it(_simple_polygon_list.begin()++);
238 it != _simple_polygon_list.end();
239 ++it)
240 {
241 if ((*it)->isPntInPolygon(pnt))
242 {
243 return true;
244 }
245 }
246 }
247 return false;
248}
249
251{
252 std::vector<GeoLib::Point> s(getAllIntersectionPoints(*this, segment));
253
254 GeoLib::Point const& a{segment.getBeginPoint()};
255 GeoLib::Point const& b{segment.getEndPoint()};
256 // no intersections -> check if at least one point of segment is in polygon
257 if (s.empty())
258 {
259 return (isPntInPolygon(a));
260 }
261
262 const double tol(std::numeric_limits<float>::epsilon());
263
264 // one intersection, intersection in line segment end point
265 if (s.size() == 1)
266 {
267 const double sqr_dist_as(MathLib::sqrDist(a, s[0]));
268 if (sqr_dist_as < tol)
269 {
270 return (isPntInPolygon(b));
271 }
272
273 const double sqr_dist_bs(MathLib::sqrDist(b, s[0]));
274 if (sqr_dist_bs < tol)
275 {
276 return (isPntInPolygon(a));
277 }
278 }
279
280 // Sorting the intersection with respect to the distance to the point a.
281 // This induces a partition of the line segment into sub segments.
282 std::sort(s.begin(), s.end(),
283 [&a](GeoLib::Point const& p0, GeoLib::Point const& p1)
284 { return MathLib::sqrDist(a, p0) < MathLib::sqrDist(a, p1); });
285
286 // remove sub segments with almost zero length
287 for (std::size_t k(0); k < s.size() - 1;)
288 {
289 if (MathLib::sqrDist(s[k], s[k + 1]) < tol)
290 {
291 s.erase(s.begin() + k + 1);
292 }
293 else
294 {
295 k++;
296 }
297 }
298
299 // Check if all sub segments are within the polygon.
300 if (!isPntInPolygon(GeoLib::Point(0.5 * (a[0] + s[0][0]),
301 0.5 * (a[1] + s[0][1]),
302 0.5 * (a[2] + s[0][2]))))
303 {
304 return false;
305 }
306 const std::size_t n_sub_segs(s.size() - 1);
307 for (std::size_t k(0); k < n_sub_segs; k++)
308 {
309 if (!isPntInPolygon(GeoLib::Point(0.5 * (s[k][0] + s[k + 1][0]),
310 0.5 * (s[k][1] + s[k + 1][1]),
311 0.5 * (s[k][2] + s[k + 1][2]))))
312 {
313 return false;
314 }
315 }
316 return isPntInPolygon(GeoLib::Point(0.5 * (s[0][0] + b[0]),
317 0.5 * (s[0][1] + b[1]),
318 0.5 * (s[0][2] + b[2])));
319}
320
322{
323 return std::all_of(ply.begin(), ply.end(),
324 [this](auto const& segment)
325 { return containsSegment(segment); });
326}
327
329{
330 const std::size_t ply_size(ply.getNumberOfPoints());
331 // check points
332 for (std::size_t k(0); k < ply_size; k++)
333 {
334 if (isPntInPolygon(*(ply.getPoint(k))))
335 {
336 return true;
337 }
338 }
339
340 auto polygon_segment_intersects_line = [&](auto const& polygon_seg)
341 {
343 return std::any_of(ply.begin(), ply.end(),
344 [&polygon_seg, &s](auto const& polyline_seg) {
345 return GeoLib::lineSegmentIntersect(
346 polyline_seg, polygon_seg, s);
347 });
348 };
349
350 return std::any_of(std::cbegin(*this), std::cend(*this),
351 polygon_segment_intersects_line);
352}
353
355 GeoLib::LineSegment const& seg, GeoLib::Point& intersection_pnt,
356 std::size_t& seg_num) const
357{
358 if (_simple_polygon_list.size() == 1)
359 {
360 for (auto seg_it(begin() + seg_num); seg_it != end(); ++seg_it)
361 {
362 if (GeoLib::lineSegmentIntersect(*seg_it, seg, intersection_pnt))
363 {
364 seg_num = seg_it.getSegmentNumber();
365 return true;
366 }
367 }
368 }
369 else
370 {
371 for (auto const* polygon : _simple_polygon_list)
372 {
373 for (auto seg_it(polygon->begin()); seg_it != polygon->end();
374 ++seg_it)
375 {
376 if (GeoLib::lineSegmentIntersect(*seg_it, seg,
377 intersection_pnt))
378 {
379 seg_num = seg_it.getSegmentNumber();
380 return true;
381 }
382 }
383 }
384 }
385 return false;
386}
387
389{
390 // *** pre processing: rotate points to xy-plan
391 // *** copy points to vector - last point is identical to the first
392 std::size_t n_pnts(this->getNumberOfPoints() - 1);
393 std::vector<GeoLib::Point*> tmp_polygon_pnts;
394 for (std::size_t k(0); k < n_pnts; k++)
395 {
396 tmp_polygon_pnts.push_back(new GeoLib::Point(*(this->getPoint(k))));
397 }
398
399 // rotate copied points into x-y-plane
400 GeoLib::rotatePointsToXY(tmp_polygon_pnts);
401
402 for (auto& tmp_polygon_pnt : tmp_polygon_pnts)
403 {
404 (*tmp_polygon_pnt)[2] =
405 0.0; // should be -= d but there are numerical errors
406 }
407
408 // *** get the left most upper point
409 std::size_t min_x_max_y_idx(0); // for orientation check
410 for (std::size_t k(0); k < n_pnts; k++)
411 {
412 if ((*(tmp_polygon_pnts[k]))[0] <=
413 (*(tmp_polygon_pnts[min_x_max_y_idx]))[0])
414 {
415 if ((*(tmp_polygon_pnts[k]))[0] <
416 (*(tmp_polygon_pnts[min_x_max_y_idx]))[0])
417 {
418 min_x_max_y_idx = k;
419 }
420 else if ((*(tmp_polygon_pnts[k]))[1] >
421 (*(tmp_polygon_pnts[min_x_max_y_idx]))[1])
422 {
423 min_x_max_y_idx = k;
424 }
425 }
426 }
427 // *** determine orientation
428 GeoLib::Orientation orient;
429 if (0 < min_x_max_y_idx && min_x_max_y_idx < n_pnts - 2)
430 {
431 orient = GeoLib::getOrientation(*tmp_polygon_pnts[min_x_max_y_idx - 1],
432 *tmp_polygon_pnts[min_x_max_y_idx],
433 *tmp_polygon_pnts[min_x_max_y_idx + 1]);
434 }
435 else
436 {
437 if (0 == min_x_max_y_idx)
438 {
439 orient = GeoLib::getOrientation(*tmp_polygon_pnts[n_pnts - 1],
440 *tmp_polygon_pnts[0],
441 *tmp_polygon_pnts[1]);
442 }
443 else
444 {
445 orient = GeoLib::getOrientation(*tmp_polygon_pnts[n_pnts - 2],
446 *tmp_polygon_pnts[n_pnts - 1],
447 *tmp_polygon_pnts[0]);
448 }
449 }
450
451 if (orient != GeoLib::CCW)
452 {
454 }
455
456 for (std::size_t k(0); k < n_pnts; k++)
457 {
458 delete tmp_polygon_pnts[k];
459 }
460}
461
463 const std::list<Polygon*>::const_iterator& polygon_it)
464{
465 GeoLib::Polyline::SegmentIterator seg_it0((*polygon_it)->begin());
466 GeoLib::Polyline::SegmentIterator seg_it1((*polygon_it)->begin());
467 GeoLib::Point intersection_pnt;
468 if (!GeoLib::lineSegmentsIntersect(*polygon_it, seg_it0, seg_it1,
469 intersection_pnt))
470 {
471 return;
472 }
473
474 std::size_t idx0(seg_it0.getSegmentNumber());
475 std::size_t idx1(seg_it1.getSegmentNumber());
476 // adding intersection point to pnt_vec
477 std::size_t const intersection_pnt_id(_ply_pnts.size());
478 const_cast<std::vector<Point*>&>(_ply_pnts).push_back(
479 new GeoLib::Point(intersection_pnt));
480
481 // split Polygon
482 if (idx0 > idx1)
483 {
484 std::swap(idx0, idx1);
485 }
486
487 GeoLib::Polyline polyline0{(*polygon_it)->getPointsVec()};
488 for (std::size_t k(0); k <= idx0; k++)
489 {
490 polyline0.addPoint((*polygon_it)->getPointID(k));
491 }
492 polyline0.addPoint(intersection_pnt_id);
493 for (std::size_t k(idx1 + 1); k < (*polygon_it)->getNumberOfPoints(); k++)
494 {
495 polyline0.addPoint((*polygon_it)->getPointID(k));
496 }
497
498 GeoLib::Polyline polyline1{(*polygon_it)->getPointsVec()};
499 polyline1.addPoint(intersection_pnt_id);
500 for (std::size_t k(idx0 + 1); k <= idx1; k++)
501 {
502 polyline1.addPoint((*polygon_it)->getPointID(k));
503 }
504 polyline1.addPoint(intersection_pnt_id);
505
506 // remove the polygon except the first
507 if (*polygon_it != this)
508 {
509 delete *polygon_it;
510 }
511 // erase polygon_it and add two new polylines
512 auto polygon1_it = _simple_polygon_list.insert(
513 _simple_polygon_list.erase(polygon_it), new GeoLib::Polygon(polyline1));
514 auto polygon0_it = _simple_polygon_list.insert(
515 polygon1_it, new GeoLib::Polygon(polyline0));
516
517 splitPolygonAtIntersection(polygon0_it);
518 splitPolygonAtIntersection(polygon1_it);
519}
520
522 const std::list<GeoLib::Polygon*>::iterator& polygon_it)
523{
524 std::size_t const n((*polygon_it)->getNumberOfPoints() - 1);
525 std::vector<std::size_t> id_vec(n);
526 std::vector<std::size_t> perm(n);
527 for (std::size_t k(0); k < n; k++)
528 {
529 id_vec[k] = (*polygon_it)->getPointID(k);
530 perm[k] = k;
531 }
532
533 BaseLib::quicksort(id_vec, 0, n, perm);
534
535 for (std::size_t k(0); k < n - 1; k++)
536 {
537 if (id_vec[k] == id_vec[k + 1])
538 {
539 std::size_t idx0 = perm[k];
540 std::size_t idx1 = perm[k + 1];
541
542 if (idx0 > idx1)
543 {
544 std::swap(idx0, idx1);
545 }
546
547 // create two closed polylines
548 GeoLib::Polyline polyline0{*(*polygon_it)};
549 for (std::size_t j(0); j <= idx0; j++)
550 {
551 polyline0.addPoint((*polygon_it)->getPointID(j));
552 }
553 for (std::size_t j(idx1 + 1);
554 j < (*polygon_it)->getNumberOfPoints();
555 j++)
556 {
557 polyline0.addPoint((*polygon_it)->getPointID(j));
558 }
559
560 GeoLib::Polyline polyline1{*(*polygon_it)};
561 for (std::size_t j(idx0); j <= idx1; j++)
562 {
563 polyline1.addPoint((*polygon_it)->getPointID(j));
564 }
565
566 // remove the polygon except the first
567 if (*polygon_it != this)
568 {
569 delete *polygon_it;
570 }
571 // erase polygon_it and add two new polygons
572 auto polygon1_it = _simple_polygon_list.insert(
573 _simple_polygon_list.erase(polygon_it), new Polygon(polyline1));
574 auto polygon0_it = _simple_polygon_list.insert(
575 polygon1_it, new Polygon(polyline0));
576
577 splitPolygonAtPoint(polygon0_it);
578 splitPolygonAtPoint(polygon1_it);
579
580 return;
581 }
582 }
583}
584
585bool operator==(Polygon const& lhs, Polygon const& rhs)
586{
587 if (lhs.getNumberOfPoints() != rhs.getNumberOfPoints())
588 {
589 return false;
590 }
591
592 const std::size_t n(lhs.getNumberOfPoints());
593 const std::size_t start_pnt(lhs.getPointID(0));
594
595 // search start point of first polygon in second polygon
596 bool nfound(true);
597 std::size_t k(0);
598 for (; k < n - 1 && nfound; k++)
599 {
600 if (start_pnt == rhs.getPointID(k))
601 {
602 nfound = false;
603 break;
604 }
605 }
606
607 // case: start point not found in second polygon
608 if (nfound)
609 {
610 return false;
611 }
612
613 // *** determine direction
614 // opposite direction
615 if (k == n - 2)
616 {
617 for (k = 1; k < n - 1; k++)
618 {
619 if (lhs.getPointID(k) != rhs.getPointID(n - 1 - k))
620 {
621 return false;
622 }
623 }
624 return true;
625 }
626
627 // same direction - start point of first polygon at arbitrary position in
628 // second polygon
629 if (lhs.getPointID(1) == rhs.getPointID(k + 1))
630 {
631 std::size_t j(k + 2);
632 for (; j < n - 1; j++)
633 {
634 if (lhs.getPointID(j - k) != rhs.getPointID(j))
635 {
636 return false;
637 }
638 }
639 j = 0; // new start point at second polygon
640 for (; j < k + 1; j++)
641 {
642 if (lhs.getPointID(n - (k + 2) + j + 1) != rhs.getPointID(j))
643 {
644 return false;
645 }
646 }
647 return true;
648 }
649 // opposite direction with start point of first polygon at arbitrary
650 // position
651 // *** ATTENTION
652 WARN(
653 "operator==(Polygon const& lhs, Polygon const& rhs) - not tested case "
654 "(implementation is probably buggy) - please contact "
655 "thomas.fischer@ufz.de mentioning the problem.");
656 // in second polygon
657 if (lhs.getPointID(1) == rhs.getPointID(k - 1))
658 {
659 std::size_t j(k - 2);
660 for (; j > 0; j--)
661 {
662 if (lhs.getPointID(k - 2 - j) != rhs.getPointID(j))
663 {
664 return false;
665 }
666 }
667 // new start point at second polygon - the point n-1 of a polygon is
668 // equal to the first point of the polygon (for this reason: n-2)
669 j = n - 2;
670 for (; j > k - 1; j--)
671 {
672 if (lhs.getPointID(n - 2 + j + k - 2) != rhs.getPointID(j))
673 {
674 return false;
675 }
676 }
677 return true;
678 }
679 return false;
680}
681
682std::list<Polygon*> const& Polygon::computeListOfSimplePolygons()
683{
686
687 for (auto polygon : _simple_polygon_list)
688 {
689 polygon->initialise();
690 }
692}
693
694} // end namespace GeoLib
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
GeoLib::Point const & getBeginPoint() const
GeoLib::Point const & getEndPoint() const
std::list< Polygon * > const & computeListOfSimplePolygons()
Definition Polygon.cpp:682
bool isPolylineInPolygon(const Polyline &ply) const
Definition Polygon.cpp:321
void splitPolygonAtPoint(const std::list< Polygon * >::iterator &polygon_it)
Definition Polygon.cpp:521
bool isPntInPolygon(MathLib::Point3d const &pnt) const
Definition Polygon.cpp:194
void splitPolygonAtIntersection(const std::list< Polygon * >::const_iterator &polygon_it)
Definition Polygon.cpp:462
bool isPartOfPolylineInPolygon(const Polyline &ply) const
Definition Polygon.cpp:328
bool containsSegment(GeoLib::LineSegment const &segment) const
Definition Polygon.cpp:250
~Polygon() override
Definition Polygon.cpp:147
std::list< Polygon * > _simple_polygon_list
Definition Polygon.h:96
bool getNextIntersectionPointPolygonLine(GeoLib::LineSegment const &seg, GeoLib::Point &intersection_pnt, std::size_t &seg_num) const
Definition Polygon.cpp:354
bool initialise()
Definition Polygon.cpp:160
void ensureCCWOrientation()
Definition Polygon.cpp:388
Polygon(const Polyline &ply, bool init=true)
Definition Polygon.cpp:124
std::size_t getSegmentNumber() const
Definition Polyline.cpp:376
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:29
std::size_t getPointID(std::size_t const i) const
Definition Polyline.cpp:149
friend class Polygon
Definition Polyline.h:79
std::size_t getNumberOfPoints() const
Definition Polyline.cpp:98
const Point * getPoint(std::size_t i) const
returns the i-th point contained in the polyline
Definition Polyline.cpp:168
void reverseOrientation()
Definition Polyline.cpp:345
SegmentIterator begin() const
Definition Polyline.h:177
bool isClosed() const
Definition Polyline.cpp:108
virtual bool addPoint(std::size_t pnt_id)
Definition Polyline.cpp:24
const std::vector< Point * > & _ply_pnts
Definition Polyline.h:200
Polyline(const std::vector< Point * > &pnt_vec)
Definition Polyline.cpp:17
std::vector< std::size_t > const & getPolylinePointIDs() const
Definition Polyline.h:204
SegmentIterator end() const
Definition Polyline.h:179
std::vector< Point * > const & getPointsVec() const
Definition Polyline.cpp:174
void quicksort(It1 first1, It1 last1, It2 first2, Comparator compare)
Definition quicksort.h:17
bool lineSegmentsIntersect(const GeoLib::Polyline *ply, GeoLib::Polyline::SegmentIterator &seg_it0, GeoLib::Polyline::SegmentIterator &seg_it1, GeoLib::Point &intersection_pnt)
std::vector< GeoLib::Point > getAllIntersectionPoints(Polygon const &polygon, GeoLib::LineSegment const &segment)
Definition Polygon.cpp:178
Eigen::Matrix3d rotatePointsToXY(InputIterator1 p_pnts_begin, InputIterator1 p_pnts_end, InputIterator2 r_pnts_begin, InputIterator2 r_pnts_end)
Location getLocationOfPoint(MathLib::Point3d const &source, MathLib::Point3d const &destination, MathLib::Point3d const &pnt)
Definition Polygon.cpp:44
bool operator==(LineSegment const &s0, LineSegment const &s1)
EdgeType getEdgeType(MathLib::Point3d const &a, MathLib::Point3d const &b, MathLib::Point3d const &pnt)
Definition Polygon.cpp:91
@ INESSENTIAL
INESSENTIAL.
Definition Polygon.cpp:31
@ CROSSING
CROSSING.
Definition Polygon.cpp:30
@ TOUCHING
TOUCHING.
Definition Polygon.cpp:29
bool lineSegmentIntersect(GeoLib::LineSegment const &s0, GeoLib::LineSegment const &s1, GeoLib::Point &s)
Orientation getOrientation(MathLib::Point3d const &p0, MathLib::Point3d const &p1, MathLib::Point3d const &p2)
double sqrDist(MathLib::Point3d const &p0, MathLib::Point3d const &p1)
Definition Point3d.cpp:19