Fishnet 2.0.1
Framework for Graph-Based Analysis of GIS Vector Data
Loading...
Searching...
No Matches
PolygonNeighbours.hpp
Go to the documentation of this file.
1#pragma once
2#include "SweepLine.hpp"
6#include "PolygonDistance.hpp"
8
9#include <unordered_map>
10
11namespace fishnet::geometry {
12
13namespace __impl {
14
22template<IPolygon P>
23using PolygonNeighbours = SweepLine<BoundingBoxWrapper<P>,std::vector<std::pair<P,P>>,HorizontalAABBOrdering<BoundingBoxWrapper<P>>,true>;
24
25template<IPolygon P>
26struct PolygonNeighboursInsertEvent : public PolygonNeighbours<P>::DefaultInsertEvent {
27 PolygonNeighboursInsertEvent(const BoundingBoxWrapper<P> & box):PolygonNeighbours<P>::DefaultInsertEvent(box){}
28 virtual fishnet::math::DEFAULT_NUMERIC eventPoint() const noexcept {
29 return this->obj->getBoundingBox().top();
30 }
31};
32
38template<IPolygon P>
39struct PolygonNeighboursRemoveEvent: public PolygonNeighbours<P>::RemoveEvent {
40 util::BiPredicate_t<BoundingBoxWrapper<P>> neighbouringPredicate; // BiPredicate deciding if two polygons are adjacent
41 size_t maxNeighbours;
42 PolygonNeighboursRemoveEvent(const BoundingBoxWrapper<P> & bbPptr, util::BiPredicate<BoundingBoxWrapper<P>> auto const & neighbouringBiPredicate,size_t maxNeighbours):PolygonNeighbours<P>::RemoveEvent(bbPptr),neighbouringPredicate(neighbouringBiPredicate),maxNeighbours(maxNeighbours){}
43
44 virtual fishnet::math::DEFAULT_NUMERIC eventPoint() const noexcept {
45 return this->obj->getBoundingBox().bottom();
46 }
47
48 virtual void process(PolygonNeighbours<P> & sweepLine, std::vector<std::pair<P,P>> & output) const {
49 const auto & sls = sweepLine.getSLS();
50 const auto & current = *this->obj;
51 auto distanceMapper = [&current](const auto & p){
52 return shapeDistance(current.getPolygon(),p);
53 };
54 // auto itInRange = [&current](auto it){
55 // return current.getBoundingBox().left() <= (*it)->getBoundingBox().right() ||
56 // current.getBoundingBox().right() >= (*it)->getBoundingBox().left();
57 // };
58 auto closestNeighbours = util::FixedSizeBuffer<P,std::invoke_result_t<decltype(distanceMapper),P>>(maxNeighbours,distanceMapper);
59 bool skippedSameObject = false; // skip same Polygon object, since it is returned as the lower_bound in the first iteration
60 for(auto it = sls.lower_bound(this->obj); it != sls.end(); --it){
61 const auto & neighbour = *(*it);
62 if(skippedSameObject && neighbouringPredicate(current,neighbour)){
63 closestNeighbours.push(neighbour.getPolygon());
64 // output.push_back(std::make_pair(current.getPolygon(),neighbour.getPolygon()));
65 }
66 skippedSameObject = true;
67 if(it == sls.begin())
68 break;
69 }
70 for(auto it = sls.upper_bound(this->obj); it != sls.end();++it){
71 const auto & neighbour = *(*it);
72 if(neighbouringPredicate(current,neighbour))
73 closestNeighbours.push(neighbour.getPolygon());
74 // output.push_back(std::make_pair(current.getPolygon(),neighbour.getPolygon()));
75 }
76 for(auto && neighbour: closestNeighbours) {
77 output.emplace_back(current.getPolygon(),std::move(neighbour));
78 }
79 sweepLine.removeSLS(this->obj);
80 }
81};
82
83template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
84static std::vector<std::pair<P,P>> polygonNeighboursDelaunay(const R & polygons, util::BiPredicate<P> auto const & neighbouringPredicate, double tolerance = 0.0, bool onlyEdges = true) {
85 // 1. Extract centroids and build centroid -> polygon map
86 std::vector<Vec2DReal> centroids;
87 centroids.reserve(util::size(polygons));
88 std::unordered_map<Vec2DReal, P, std::hash<Vec2DReal>, std::equal_to<Vec2DReal>> centroidToPolygon;
89 for (const auto & polygon : polygons) {
90 auto c = polygon.centroid();
91 centroids.push_back(c);
92 centroidToPolygon.emplace(c, polygon);
93 }
94
95 // 2. Delaunay triangulation on centroids
96 DelaunayTriangulation delaunay(centroids, tolerance, onlyEdges);
97
98 // 3. Map edges back to polygons and apply predicate
99 return delaunay.edges().transform([&](const auto & edges) {
100 std::vector<std::pair<P,P>> adjacencies;
101 for (const auto & edge : edges) {
102 auto it1 = centroidToPolygon.find(edge.p());
103 auto it2 = centroidToPolygon.find(edge.q());
104 if (it1 != centroidToPolygon.end() && it2 != centroidToPolygon.end() && neighbouringPredicate(it1->second, it2->second)) {
105 adjacencies.emplace_back(it1->second, it2->second);
106 }
107 }
108 return adjacencies;
109 }).value_or(std::vector<std::pair<P,P>>{});
110}
111
112template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
113static std::vector<std::pair<P,P>> polygonNeighboursSweep(const R & polygons, util::BiPredicate<BoundingBoxWrapper<P>> auto const & neighbouringPredicate,util::UnaryFunction<P,BoundingBoxWrapper<P>> auto const & wrapper, size_t maxNeighbours) {
114 using SweepLine_t = typename __impl::PolygonNeighbours<P>;
115 SweepLine_t sweepLine;
116 std::vector<std::pair<P,P>> output;
117 std::vector<BoundingBoxWrapper<P>> boundingBoxPolygons;
118 boundingBoxPolygons.reserve(util::size(polygons));
119 std::ranges::for_each(polygons,[&boundingBoxPolygons,&wrapper](const auto & p){
120 boundingBoxPolygons.push_back(wrapper(p)); // wrap each polygon in a BoundingBoxWrapper
121 });
122 std::ranges::for_each(boundingBoxPolygons,[&sweepLine,&neighbouringPredicate,maxNeighbours](const auto & bbPptr){
123 sweepLine.addEvent(std::make_unique<__impl::PolygonNeighboursInsertEvent<P>>(bbPptr)); // add insert events to sweepline
124 sweepLine.addEvent(std::make_unique<__impl::PolygonNeighboursRemoveEvent<P>>(bbPptr,neighbouringPredicate,maxNeighbours)); // add remove events to sweepline
125 });
126 return sweepLine.sweep(output);
127}
128
129} // namespace __impl
130
132public:
133
147template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
148static std::vector<std::pair<P,P>> delaunay(const R & polygons, util::BiPredicate<P> auto const & neighbouringPredicate, double tolerance = 0.0, bool onlyEdges = true) {
149 return __impl::polygonNeighboursDelaunay(polygons,neighbouringPredicate, tolerance, onlyEdges);
150}
151
156template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
157static std::vector<std::pair<P,P>> delaunay(const R & polygons) {
158 return delaunay(polygons, fishnet::util::TrueBiPredicate{});
159}
160
171template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
172static std::vector<std::pair<P,P>> sweepTemplate(const R & polygons, util::BiPredicate<BoundingBoxWrapper<P>> auto const & neighbouringPredicate,util::UnaryFunction<P,BoundingBoxWrapper<P>> auto const & wrapper, size_t maxNeighbours) {
173 return __impl::polygonNeighboursSweep(polygons,neighbouringPredicate,wrapper,maxNeighbours);
174}
175
186template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
187static std::vector<std::pair<P,P>> sweep(const R & polygons, util::BiPredicate<P> auto && neighbouringPredicate,util::UnaryFunction<P,BoundingBoxWrapper<P>> auto const & wrapper,size_t maxNeighbours) {
188 return sweepTemplate(polygons, [&neighbouringPredicate](const BoundingBoxWrapper<P> & current, const BoundingBoxWrapper<P> & neighbour){
189 return neighbouringPredicate(current.getPolygon(),neighbour.getPolygon());
190 },wrapper,maxNeighbours);
191}
192
202template<PolygonRange R, IPolygon P = std::ranges::range_value_t<R>>
203static std::vector<std::pair<P,P>> sweep(const R & polygons, util::BiPredicate<P> auto const & neighbouringPredicate,size_t maxNeighbours) {
204 return sweep(polygons,neighbouringPredicate,[](const P & p){return BoundingBoxWrapper(p);},maxNeighbours);
205}
206
215template<PolygonRange R>
216static std::vector<std::pair<std::ranges::range_value_t<R>,std::ranges::range_value_t<R>>> sweepWithFixedBuffer(const R & polygons, fishnet::math::DEFAULT_NUMERIC bufferMultiplier, size_t maxNeighbours) {
217 if (bufferMultiplier <= 1)
218 throw std::invalid_argument("Buffer range multiplier has to be greater than 1");
219 using P = std::ranges::range_value_t<R>;
220
221 auto crossesOrContainedInBoundingBox = [](const BoundingBoxWrapper<P> & current,const BoundingBoxWrapper<P> & neighbour){
222 return neighbour.getBoundingBox().crosses(current.getBoundingBox()) || neighbour.getBoundingBox().contains(current.getBoundingBox()) || current.getBoundingBox().contains(neighbour.getBoundingBox());
223 }; // polygons are in relation if (scaled) bounding boxes overlap
224
225 auto scaledWrapper = [bufferMultiplier](const P & polygon) {
226 auto aaBBRectangle = Rectangle<fishnet::math::DEFAULT_NUMERIC>(polygon);
227 return BoundingBoxWrapper(polygon,aaBBRectangle.scale(bufferMultiplier));
228 };
229 return sweep(polygons, crossesOrContainedInBoundingBox,scaledWrapper,maxNeighbours);
230}
231};
232} // namespace fishnet::geometry
Wrapper for Polygon objects, which stores a (custom) bounding box.
Definition BoundingBoxWrapper.hpp:13
const Rectangle< fishnet::math::DEFAULT_NUMERIC > & getBoundingBox() const noexcept
Definition BoundingBoxWrapper.hpp:36
auto getPolygon() const noexcept
Definition BoundingBoxWrapper.hpp:40
Implementation of a rectangle Stores top,right,bottom and left point of the rectangle.
Definition Rectangle.hpp:15
constexpr bool contains(IPoint auto const &point) const noexcept
Definition Ring.hpp:196
virtual fishnet::math::DEFAULT_NUMERIC eventPoint() const noexcept=0
const Input * obj
Definition SweepLine.hpp:38
virtual void process(SweepLine< Input, Output, SLSLess, InsertEventsFirst, EventQueueGreater > &sweepLine, Output &output) const =0
Definition FunctionalConcepts.hpp:42
Definition FunctionalConcepts.hpp:9
Definition BoundingBoxWrapper.hpp:5
DelaunayTriangulation(R const &) -> DelaunayTriangulation< std::ranges::range_value_t< R > >
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_NUMERIC
Definition NumericConcepts.hpp:13
constexpr size_t size(std::ranges::range auto &&range) noexcept
Definition CollectionConcepts.hpp:50
Definition PolygonNeighbours.hpp:131
static std::vector< std::pair< P, P > > delaunay(const R &polygons)
Overload without predicate — returns all Delaunay edges.
Definition PolygonNeighbours.hpp:157
static std::vector< std::pair< P, P > > sweep(const R &polygons, util::BiPredicate< P > auto const &neighbouringPredicate, size_t maxNeighbours)
Overload with wrapper into default BoundingBoxWrappers (no custom bounding box allowed)
Definition PolygonNeighbours.hpp:203
static std::vector< std::pair< P, P > > sweepTemplate(const R &polygons, util::BiPredicate< BoundingBoxWrapper< P > > auto const &neighbouringPredicate, util::UnaryFunction< P, BoundingBoxWrapper< P > > auto const &wrapper, size_t maxNeighbours)
Generic findNeighbouringPolygons function, which returns a list of pairs indicating the adjacencies o...
Definition PolygonNeighbours.hpp:172
static std::vector< std::pair< P, P > > sweep(const R &polygons, util::BiPredicate< P > auto &&neighbouringPredicate, util::UnaryFunction< P, BoundingBoxWrapper< P > > auto const &wrapper, size_t maxNeighbours)
Finding neighbours of polygons using a sweepline, returns a list of pairs indicating the adjacencies ...
Definition PolygonNeighbours.hpp:187
static std::vector< std::pair< std::ranges::range_value_t< R >, std::ranges::range_value_t< R > > > sweepWithFixedBuffer(const R &polygons, fishnet::math::DEFAULT_NUMERIC bufferMultiplier, size_t maxNeighbours)
Overload for finding neighbouring polygons with a custom buffer multiplier for the bounding boxes.
Definition PolygonNeighbours.hpp:216
static std::vector< std::pair< P, P > > delaunay(const R &polygons, util::BiPredicate< P > auto const &neighbouringPredicate, double tolerance=0.0, bool onlyEdges=true)
Find neighbouring polygons using Delaunay triangulation on centroids.
Definition PolygonNeighbours.hpp:148
DefaultInsertEvent(const Input &obj)
Definition SweepLine.hpp:71
RemoveEvent(const Input &obj)
Definition SweepLine.hpp:64
Definition FunctionalConcepts.hpp:83