Fishnet 2.0.1
Framework for Graph-Based Analysis of GIS Vector Data
Loading...
Searching...
No Matches
Polygon.hpp
Go to the documentation of this file.
1#pragma once
3#include "Ring.hpp"
4#include "SimplePolygon.hpp"
5
6
7namespace fishnet::geometry {
13template<fishnet::math::Number T>
14class Polygon : public SimplePolygon<T>{
15public:
17private:
18 std::vector<Ring<T>> holes;
19
26 constexpr PointLocation polygonPointLocation(IPoint auto const & point) const noexcept {
27 PointLocation ofBoundary = this->getPointLocation(point);
28 if (ofBoundary != PointLocation::INSIDE) {
29 return ofBoundary;
30 }
31 if (std::ranges::any_of(getHoles(),[point](const auto & hole){
32 return hole.isInside(point);
33 })) return PointLocation::OUTSIDE;
35
36 }
37
38 constexpr static std::vector<Ring<T>> copyRings(util::forward_range_of<Ring<T>> auto const & ringRange) noexcept {
39 std::vector<Ring<T>> rings;
40 std::ranges::copy(ringRange, std::back_inserter(rings));
41 return rings;
42 }
43
44 constexpr bool equals(IPolygon auto const & other) const noexcept {
45 return this->getBoundary() == other.getBoundary() && std::ranges::is_permutation(this->getHoles(),other.getHoles());
46 }
47
48public:
49 using numeric_type = T;
50 constexpr static GeometryType type = GeometryType::POLYGON;
51
58 Polygon(const Ring<T> & boundary, const std::vector<Ring<T>> & holes = {}, bool checked = false):SimplePolygon<T>(boundary),holes(holes){
59 if(checked)
60 return;
61 if (std::ranges::any_of(holes, [&boundary](const auto & hole){return not boundary.contains(hole);}))
62 throw InvalidGeometryException("Hole not contained within Boundary of Polygon");
63 if (std::ranges::any_of(holes, [&boundary,&holes](const auto & h1){
64 return std::ranges::any_of(holes, [&boundary,&h1](const auto & h2){
65 return h1.crosses(h2);
66 });
67 })) throw InvalidGeometryException("Holes of Polygon are intersecting each other");
68 };
69
70 Polygon(const Ring<T> & boundary, const util::forward_range_of<Ring<T>> auto & holes, bool checked = false):Polygon(boundary,std::move(copyRings(holes)), checked) {}
71
72 Polygon(const SimplePolygon<T> & boundary, const std::vector<Ring<T>> & holes = {}, bool checked = false):Polygon(boundary.getBoundary(),holes, checked){}
73
74 Polygon(const SimplePolygon<T> & boundary, const util::forward_range_of<Ring<T>> auto & holes, bool checked = false):Polygon(boundary.getBoundary(),std::move(copyRings(holes)), checked) {}
75
76 constexpr const Ring<T> & getBoundary() const noexcept{
77 return static_cast<const Ring<T> &>(*this);
78 }
79
80 constexpr util::view_of<Ring<T>> auto getHoles() const noexcept {
81 return std::views::all(holes);
82 }
83
84 constexpr util::view_of<Segment<T>> auto getSegments() = delete;
85
87
88
89 constexpr bool isSimple() const noexcept {
90 return holes.size() == 0;
91 }
92
93 constexpr const SimplePolygon<T> & toSimple() const noexcept {
94 return static_cast<const SimplePolygon<T> & >(*this);
95 }
96
102 constexpr fishnet::math::DEFAULT_FLOATING_POINT area() const noexcept {
104 for(const auto & hole : holes) {
105 area -= hole.area();
106 }
107 return area;
108 }
109
118 constexpr Vec2DReal centroid() const noexcept {
119 auto polygonArea = this->area();
120 auto accCentroid = this->getBoundary().centroid() * (this->getBoundary().area() / polygonArea);
121 for(const auto & hole: this->getHoles()){
122 accCentroid = accCentroid + hole.centroid() * (-hole.area() / polygonArea);
123 }
124 return accCentroid; // -> weighted centroid by decomposition
125 }
126
127 constexpr bool inline contains(IPoint auto const & point) const noexcept {
128 auto location = polygonPointLocation(point);
129 return location == PointLocation::INSIDE || location == PointLocation::BOUNDARY;
130 }
131
132 constexpr bool inline contains(ISegment auto const & segment) const noexcept {
133 return this->getBoundary().contains(segment) && std::ranges::none_of(this->getHoles(),[&segment](const auto & hole){return hole.intersects(segment);});
134 }
135
136 constexpr bool inline isInside(IPoint auto const & point) const noexcept {
137 return polygonPointLocation(point) == PointLocation::INSIDE;
138 }
139
140 constexpr bool inline isOnBoundary(IPoint auto const & point) const noexcept {
141 return polygonPointLocation(point) == PointLocation::BOUNDARY;
142 }
143
144 constexpr bool inline isOutside(IPoint auto const & point) const noexcept {
145 return polygonPointLocation(point) == PointLocation::OUTSIDE;
146 }
147
148 constexpr bool intersects(LinearGeometry auto const & linearFeature) const noexcept {
149 return this->getBoundary().intersects(linearFeature) || std::ranges::any_of(getHoles(),[linearFeature](const auto & hole){
150 return hole.intersects(linearFeature);
151 });
152 }
153
154 constexpr std::unordered_set<Vec2D<double>> intersections(LinearGeometry auto const & linearFeature) const noexcept {
155 std::unordered_set<Vec2D<double>> intersectionSet;
156 std::ranges::for_each(this->getBoundary().intersections(linearFeature),[&intersectionSet](const auto & p){intersectionSet.insert(p);});
157 std::ranges::for_each(this->getHoles(), [&intersectionSet,&linearFeature](const auto & hole){
158 std::ranges::for_each(hole.intersections(linearFeature),[&intersectionSet](const auto & p){intersectionSet.insert(p);});
159 });
160 return intersectionSet;
161 }
162
163 constexpr bool containsInHole(IPoint auto const & point) const noexcept {
164 return std::ranges::any_of(this->getHoles(),[&point](const auto & hole){
165 return hole.contains(point);
166 });
167 }
168
169 constexpr bool containsInHole(IPolygon auto const & other) const noexcept {
170 return std::ranges::any_of(this->getHoles(),[&other](const auto & hole){
171 return hole.contains(other.getBoundary());
172 });
173 }
174
175 constexpr bool contains(IPolygon auto const & other) const noexcept {
176 if(this->equals(other))
177 return true;
178 if(containsInHole(other))
179 return false; // inside of hole
180 return this->getBoundary().contains(other.getBoundary()) && std::ranges::none_of(this->getHoles(),[&other](const auto & hole){
181 // the boundary of the other polygon must not be intersected by any hole and the other polygon is not allowed to contain any hole
182 return hole.crosses(other.getBoundary()) || other.getBoundary().contains(hole);
183 });
184 }
185
186 constexpr bool crosses(IPolygon auto const & other) const noexcept {
187 if(containsInHole(other))
188 return false;
189 return this->getBoundary().crosses(other.getBoundary()) || std::ranges::any_of(this->getHoles(),[&other](const auto & hole){
190 return hole.crosses(other.getBoundary());
191 });
192 }
193
194 constexpr bool touches(IPolygon auto const & other) const noexcept {
195 if(this->crosses(other))
196 return false;
197 return this->getBoundary().touches(other.getBoundary()) || std::ranges::any_of(this->getHoles(), [&other](const auto & hole){
198 return hole.contains(other.getBoundary()) && std::ranges::any_of(other.getBoundary().getPoints(),[&hole](const auto & p){
199 return hole.isOnBoundary(p); // hole fully contains other polygon and touches it at least at one point
200 });
201 });
202 }
203
209 constexpr fishnet::math::DEFAULT_FLOATING_POINT distance(IPolygon auto const & other) const noexcept {
210 if(this->contains(other)) return 0;
211 if(this->touches(other)) return 0;
212 // check if in any hole and compute distance
213 for(const auto & hole: getHoles()){
214 if(hole.contains(other.getBoundary())){
215 return shapeDistance(hole,other.getBoundary());
216 }
217 }
218 // compute boundary distance otherwise
219 return this->getBoundary().distance(other.getBoundary());
220 }
221
222 template<fishnet::math::Number U>
223 constexpr bool operator==(const Polygon<U> & other) const noexcept {
224 return this->equals(other);
225 }
226
227 constexpr size_t hash() const noexcept{
228 auto boundaryHash = this->getBoundary().hash();
229 auto holesHashView = this->getHoles() | std::views::transform([](const auto & ring){return ring.hash();});
230 auto holesHash = std::accumulate(std::ranges::begin(holesHashView),std::ranges::end(holesHashView),size_t(0));
231 return boundaryHash ^ holesHash;
232 }
233
234 constexpr std::string toString() const noexcept {
235 std::ostringstream oss;
236 oss << "Boundary: ";
237 oss << this->getBoundary().toString();
238 oss << "\nHoles: {";
239 bool first = true;
240 for(const auto & hole : this->getHoles()){
241 if(!first) oss << "},{";
242 oss << hole.toString();
243 first = false;
244 }
245 oss << "}";
246 return oss.str();
247 }
248};
249
250//Deduction guides
251template<math::Number T>
252Polygon(const Ring<T> &, auto) -> Polygon<T>;
253
254template<math::Number T>
256
257static_assert(IPolygon<Polygon<double>>);
258static_assert(Shape<Polygon<double>>);
259// Explicit template instantiation
261} // namespace fishnet::geometry
Polygon implementation A Polygon consist of a boundary (IRing) and zero or more holes (IRing)
Definition Polygon.hpp:14
constexpr fishnet::math::DEFAULT_FLOATING_POINT area() const noexcept
Calculate the area of the polygon Subtract accumulated area of holes from the area contained within t...
Definition Polygon.hpp:102
constexpr bool operator==(const Polygon< U > &other) const noexcept
Definition Polygon.hpp:223
static constexpr GeometryType type
Definition Polygon.hpp:50
constexpr size_t hash() const noexcept
Definition Polygon.hpp:227
constexpr bool contains(IPolygon auto const &other) const noexcept
Definition Polygon.hpp:175
Polygon(const Ring< T > &boundary, const util::forward_range_of< Ring< T > > auto &holes, bool checked=false)
Definition Polygon.hpp:70
Polygon(const SimplePolygon< T > &boundary, const std::vector< Ring< T > > &holes={}, bool checked=false)
Definition Polygon.hpp:72
constexpr bool containsInHole(IPoint auto const &point) const noexcept
Definition Polygon.hpp:163
constexpr util::view_of< Segment< T > > auto getSegments()=delete
constexpr util::random_access_range_of< Vec2D< T > > auto getPoints()=delete
constexpr const SimplePolygon< T > & toSimple() const noexcept
Definition Polygon.hpp:93
constexpr std::unordered_set< Vec2D< double > > intersections(LinearGeometry auto const &linearFeature) const noexcept
Definition Polygon.hpp:154
constexpr std::string toString() const noexcept
Definition Polygon.hpp:234
constexpr bool isInside(IPoint auto const &point) const noexcept
Definition Polygon.hpp:136
Polygon(const Ring< T > &boundary, const std::vector< Ring< T > > &holes={}, bool checked=false)
Construct a new Polygon object using boundary and holes.
Definition Polygon.hpp:58
constexpr const Ring< T > & getBoundary() const noexcept
Definition Polygon.hpp:76
constexpr Vec2DReal centroid() const noexcept
Calculate weighted centroid of the polygon https://en.wikipedia.org/wiki/Centroid.
Definition Polygon.hpp:118
constexpr bool isOutside(IPoint auto const &point) const noexcept
Definition Polygon.hpp:144
Polygon(const SimplePolygon< T > &boundary, const util::forward_range_of< Ring< T > > auto &holes, bool checked=false)
Definition Polygon.hpp:74
constexpr bool crosses(IPolygon auto const &other) const noexcept
Definition Polygon.hpp:186
constexpr bool isOnBoundary(IPoint auto const &point) const noexcept
Definition Polygon.hpp:140
constexpr bool isSimple() const noexcept
Definition Polygon.hpp:89
constexpr bool contains(ISegment auto const &segment) const noexcept
Definition Polygon.hpp:132
constexpr bool touches(IPolygon auto const &other) const noexcept
Definition Polygon.hpp:194
T numeric_type
Definition Polygon.hpp:49
constexpr util::view_of< Ring< T > > auto getHoles() const noexcept
Definition Polygon.hpp:80
constexpr fishnet::math::DEFAULT_FLOATING_POINT distance(IPolygon auto const &other) const noexcept
Distance between the two polygons.
Definition Polygon.hpp:209
constexpr bool contains(IPoint auto const &point) const noexcept
Definition Polygon.hpp:127
constexpr bool intersects(LinearGeometry auto const &linearFeature) const noexcept
Definition Polygon.hpp:148
constexpr bool containsInHole(IPolygon auto const &other) const noexcept
Definition Polygon.hpp:169
Implementation of a ring.
Definition Ring.hpp:31
constexpr bool contains(IPoint auto const &point) const noexcept
Definition Ring.hpp:196
constexpr PointLocation getPointLocation(IPoint auto const &point) const noexcept
Get the location of a point with regard to the ring (INSIDE | OUTSIDE | BOUNDARY) Uses the Ray-Castin...
Definition Ring.hpp:80
Simple polygon implementation with no holes.
Definition SimplePolygon.hpp:13
Implementation of a two-dimensional vector.
Definition Vec2D.hpp:16
Interface for a point.
Definition IPoint.hpp:92
Interface for a polygon A polygon is a two-dimensional shape defined by a boundary and zero or more h...
Definition IPolygon.hpp:98
Interface for a segment A segment is formed between two points p and q, containing only the points on...
Definition ISegment.hpp:105
Definition IGeometry.hpp:13
A geometry enclosing an area: a ring, a polygon or a multi-polygon.
Definition IGeometry.hpp:23
Definition CollectionConcepts.hpp:27
Definition CollectionConcepts.hpp:39
Definition CollectionConcepts.hpp:45
Definition BoundingBoxWrapper.hpp:5
PointLocation
Definition Ring.hpp:21
GeometryType
Definition GeometryBase.hpp:6
static fishnet::math::DEFAULT_FLOATING_POINT shapeDistance(Shape auto const &lhs, Shape auto const &rhs, util::BiFunction< Vec2DReal, Vec2DReal, fishnet::math::DEFAULT_FLOATING_POINT > auto const &distanceFunction) noexcept
Compute the distance between two shapes, with custom distance function.
Definition PolygonDistance.hpp:465
double DEFAULT_FLOATING_POINT
Definition NumericConcepts.hpp:12
STL namespace.