Fishnet 2.0.1
Framework for Graph-Based Analysis of GIS Vector Data
Loading...
Searching...
No Matches
BaseGraph.hpp
Go to the documentation of this file.
1#pragma once
2#include <ranges>
3#include <algorithm>
6#include <fishnet/Edge.hpp>
8
9namespace fishnet::graph::__impl {
16template<Edge E, AdjacencyContainer<typename E::node_type> AdjContainer>
17class BaseGraph {
18public:
19 using node_type = typename AdjContainer::node_type;
20 using edge_type = E;
21 using adj_container_type = AdjContainer;
22private:
23 AdjContainer adj;
24 using N = node_type;
25public:
26
27
28 BaseGraph() requires std::is_default_constructible_v<AdjContainer>: adj(){};
29
30 BaseGraph(AdjContainer && adjContainer): adj(std::move(adjContainer)){}
31
32 BaseGraph(BaseGraph && other):adj(std::move(other.adj)){}
33
34 BaseGraph(const BaseGraph & other) requires std::is_copy_constructible_v<AdjContainer>
35 :adj(other.adj){}
36
37 BaseGraph & operator=(BaseGraph && other)noexcept{
38 this->adj = std::move(other.adj);
39 return *this;
40 }
41
42 BaseGraph & operator=(const BaseGraph & other) requires std::is_copy_assignable_v<AdjContainer> {
43 this->adj = other.adj;
44 return *this;
45 }
46
47 BaseGraph(util::forward_range_of<N> auto && nodes){
48 addNodes(std::forward<decltype(nodes)>(nodes));
49 };
50
51 bool addNode(const N & node){
52 return adj.addNode(node);
53 }
54
55 bool addNode(N && node){
56 return adj.addNode(node);
57 }
58
59 template<typename... Args>
60 bool addNode(const N & node, Args... args){
61 bool hasChanged = addNode(node);
62 return hasChanged && addNode(args...);
63 }
64
65 template<typename... Args>
66 bool addNode(N && node, Args... args){
67 bool hasChanged = addNode(std::move(node));
68 return hasChanged && addNode(args...);
69 }
70
71 bool addNodes(util::forward_range_of<N> auto && nodes){
72 return adj.addNodes(std::forward<decltype(nodes)>(nodes));
73 }
74
75 bool containsNode(const N & node) const noexcept {
76 return adj.contains(node);
77 }
78
79 void removeNode(const N & node) {
80 adj.removeNode(node);
81 }
82
83 bool addEdge(const N & from, const N & to){
84 if (not containsEdge(from,to)) {
85 adj.addAdjacency(from,to);
86 if constexpr(not E::isDirected()){
87 adj.addAdjacency(to,from); // add reversed edge as well if graph is undirected
88 }
89 return true;
90 }
91 return false;
92 }
93
94 bool addEdge(N && from, N && to){
95 if (not containsEdge(from,to)){
96 adj.addAdjacency(from,to);
97 if constexpr(not E::isDirected()){
98 adj.addAdjacency(to,from); // add reversed edge as well if graph is undirected
99 }
100 return true;
101 }
102 return false;
103 }
104
105 void addEdges(util::forward_range_of<std::pair<N,N>> auto && edges){
106 if constexpr(not E::isDirected()){
107 std::vector<std::pair<N,N>> reversed; // add reversed edge as well if graph is undirected
108 std::ranges::transform(edges,std::back_inserter(reversed),[]( auto & pair){return std::pair(pair.second,pair.first);});
109 adj.addAdjacencies(std::move(reversed));
110 }
111 adj.addAdjacencies(edges);
112 }
113
114 void addEdges(util::forward_range_of<E> auto && edges) {
115 std::vector<std::pair<N,N>> pairs;
116 std::ranges::for_each(edges,[&pairs](const auto & e){pairs.emplace_back(e.getFrom(),e.getTo());});
117 addEdges(pairs);
118 }
119
120 bool addEdge(const E & edge) {
121 N from = edge.getFrom();
122 N to = edge.getTo();
123 return addEdge(from,to);
124 }
125
126 bool containsEdge(const N & from, const N & to) const noexcept {
127 if constexpr(E::isDirected())
128 return adj.hasAdjacency(from,to);
129 else
130 return adj.hasAdjacency(from,to) && adj.hasAdjacency(to,from);
131 }
132
133 bool containsEdge(const E & edge) const noexcept {
134 return containsEdge(edge.getFrom(),edge.getTo());
135 }
136
137 void removeEdge(const N & from, const N & to){
138 adj.removeAdjacency(from,to);
139 if constexpr(not E::isDirected()){
140 adj.removeAdjacency(to,from);
141 }
142 }
143
144 void removeEdge(const E & edge){
145 removeEdge(edge.getFrom(),edge.getTo());
146 }
147
148 inline E makeEdge(const N & from, const N & to) const {
149 return E(from,to);
150 }
151
152 auto getNodes() const noexcept {
153 return std::views::all(adj.nodes());
154 }
155
156 auto getEdges() const {
157 std::unordered_set<E> edgeSet;
158 for(const auto & [from,to]: adj.getAdjacencyPairs()){
159 edgeSet.emplace(this->makeEdge(from,to));
160 }
161 std::vector<E> edges(edgeSet.begin(),edgeSet.end());
162 return edges;
163 }
164
165 auto getNeighbours(const N & node) const noexcept {
166 return adj.adjacency(node);
167 }
168
169 auto getReachableFrom(const N & node) const noexcept {
170 if constexpr(E::isDirected()){
171 auto isReachableFrom = [&](const N & from) {return containsEdge(from,node);};
172 return getNodes() | std::views::filter(isReachableFrom);
173 }else {
174 return getNeighbours(node);
175 }
176 }
177
178 auto getOutboundEdges(const N & node) const {
179 std::vector<E> edges;
180 if(not containsNode(node)) return edges;
181 for(const auto & neighbour : getNeighbours(node)) {
182 edges.emplace_back(this->makeEdge(node,neighbour));
183 }
184 return edges;
185 }
186
187 auto getInboundEdges(const N & node) const {
188 std::vector<E> edges;
189 if(not containsNode(node)) return edges;
190 for(const auto & neighbour : getReachableFrom(node)) {
191 edges.emplace_back(this->makeEdge(neighbour,node));
192 }
193 return edges;
194 }
195
196 void clear(){
197 adj.clear();
198 }
199
200 const AdjContainer & getAdjacencyContainer() const {
201 return adj;
202 }
203
204 virtual ~BaseGraph()=default;
205};
206}
STL namespace.