1
+ """
2
+ make_vertex_raster(A::GeoArray)
3
+
4
+ Constuct a vertex raster (a raster where the value of each pixel corresponds
5
+ to its ID in a graph, and 0s correspond to NoData). Returns a GeoArray. This
6
+ function is recommended for internal use only.
7
+
8
+ ## Parameters
9
+ `A`: The GeoArray from which a graph will be built, which is used as the
10
+ reference for building the vertex raster. Pixels with NoData (`A.missingval`)
11
+ are skipped (no vertex is assigned). Pixels with NoData will get a value of 0 in
12
+ the vertex raster.
13
+ """
14
+ function make_vertex_raster (A:: GeoData.GeoArray )
15
+ # Make an array of unique node identifiers
16
+ nodemap = zeros (Int64, size (A. data))
17
+ is_node = (A. data .!= A. missingval) .&
18
+ ((! ). (isnan .(A. data)))
19
+
20
+ nodemap[is_node] = 1 : sum (is_node)
21
+
22
+ nodemap = GeoData. GeoArray (nodemap, dims (A))
23
+
24
+ nodemap
25
+ end
26
+
27
+
1
28
"""
2
29
weightedrastergraph(
3
- A ::GeoArray;
30
+ weight_raster ::GeoArray;
4
31
directed::Bool = false,
5
32
condition::Function = is_data,
6
33
cardinal_neighbors_only::Bool = false,
7
34
connect_using_avg_raster_val::Bool = true
8
35
)
9
36
10
37
Construct a `WeightedRasterGraph` or `WeightedRasterDiGraph` (if
11
- `directed = true`) from a raster dataset.
38
+ `directed = true`) from a raster dataset. The weight raster, denotes
39
+ the edge weight correponding to each vertex. Since edges are between rather than
40
+ on vertices, edge weights are calculated as the average of the weights for each
41
+ vertex.
12
42
13
43
## Parameters
14
44
`weight_raster`: A GeoData.GeoArray contained values that, where applicable
@@ -53,27 +83,33 @@ If `false`, the weight between two nodes with resistances R1 and R2 is
53
83
calculated by converting resistance to conductances, taking the average, then
54
84
taking the inverse of the result to convert back to resistance:
55
85
`1 / ((1/R1 + 1/R2) / 2)`. `connect_using_avg_weights = false` correspondes to
56
- the default settings in Circuitscape. Defaults to `true', in which case the
57
- simple average of the weights (adjusted for distance in the case of diagonal
58
- neighbors) in `weight_raster` are used.
86
+ the default settings in Circuitscape. Defaults to `true`, in which case the
87
+ simple average (adjusted for distance in the case of diagonal
88
+ neighbors) of the weights in `weight_raster` is used.
89
+
90
+ `combine`: In the case that there are multiple edges between a single pair
91
+ of vertices, how should the weight be chosen? Defaults to `min`. See the docs
92
+ for `SparseArrays.sparse()` for more information.
59
93
"""
60
94
function weightedrastergraph (
61
95
weight_raster:: GeoArray ;
62
96
directed:: Bool = false ,
63
- condition_raster:: GeoArray = A ,
97
+ condition_raster:: GeoArray = weight_raster ,
64
98
condition:: Function = is_data,
65
99
cardinal_neighbors_only:: Bool = false ,
66
- connect_using_avg_weights:: Bool = true
100
+ connect_using_avg_weights:: Bool = true ,
101
+ combine:: Function = min
67
102
)
68
103
v = make_vertex_raster (weight_raster)
69
- g = make_weighted_graph (
104
+ g = make_raster_graph (
70
105
weight_raster,
71
- vertex_raster ,
106
+ v ,
72
107
directed = directed,
73
108
condition_raster = condition_raster,
74
109
condition = condition,
75
110
cardinal_neighbors_only = cardinal_neighbors_only,
76
- connect_using_avg_weights = connect_using_avg_weights
111
+ connect_using_avg_weights = connect_using_avg_weights,
112
+ combine = combine
77
113
)
78
114
79
115
if directed
@@ -86,30 +122,106 @@ function weightedrastergraph(
86
122
87
123
end
88
124
89
- function make_weighted_graph (
125
+ """
126
+ make_raster_graph(
127
+ weight_raster::GeoArray,
128
+ vertex_raster::GeoArray;
129
+ directed::Bool = false,
130
+ condition_raster::GeoArray = weight_raster,
131
+ condition::Function = is_data,
132
+ cardinal_neighbors_only::Bool = false,
133
+ connect_using_avg_weights::Bool = true,
134
+ combine::Function = min
135
+ )
136
+
137
+ Construct a `SimpleWeightedGraph` or `SimpleWeightedDiGraph` (if
138
+ `directed = true`) based on vertex and weight rasters. This function is useful
139
+ if you already have a custom vertex raster and don't want SpatialGraphs.jl to
140
+ make one for you. The vertex raster denotes the spatial locations of each vertex
141
+ in the graph, and the weight raster denotes the edge weight correponding to each
142
+ vertex. Since edges are between rather than on vertices, edge weights are
143
+ calculated as the average of the weights for each vertex being connected.
144
+
145
+ ## Parameters
146
+ `weight_raster`: A `GeoData.GeoArray` containing values that, where applicable
147
+ based on other arguments, determine which pixels to connect and the edge
148
+ weights between pixels. Any pixel in `weight_raster` with a value not equal to
149
+ `weight_raster.missingval` will be assigned a vertex in the graph (corresponding
150
+ to its centroid).
151
+
152
+ `vertex_raster`: A `GeoData.GeoArray` with integer values ranging from 1:n,
153
+ where n is the number of unique vertices in the graph.
154
+
155
+ ##Arguments
156
+ `directed`: A `Bool` determining whether the graph should be directed.
157
+
158
+ `condition_raster`: A raster with values that can be used to determine whether
159
+ two neighboring pixels should be connected. For example, an elevation raster
160
+ can be used to create a hydologic flow graph.
161
+
162
+ `condition`: A function that compares the values in `condition_raster` for two
163
+ neighbors to determine if those neighbors should be connected. The function must
164
+ compare two values and return either `true` or `false`. Useful functions to use
165
+ here include `<`, `<=`, `==`, etc. The first argument to `condition` corresponds
166
+ to the source vertex, and the second argument corresponds to the destination
167
+ vertex. So, if you only want to connect sources to destinations with a lower
168
+ value in `condition_raster` (e.g. in the case of developing a hydrologic flow
169
+ graph based on elevation), then you would use `<` for `condition`. Defaults to
170
+ `is_data`, which results in neighbors being connected as long as they are not
171
+ NoData (`condition_raster.missingval`) in `condition_raster`. Note that if using
172
+ an inequality function (or any function where the result depends on argument
173
+ position), the graph must be directed. For undirected graphs, you can use either
174
+ `is_data` or `==`, or any other custom function where argument position doesn't
175
+ matter, e.g. a function that determines whether the values in `condition_raster`
176
+ are within a certain distance of each other.
177
+
178
+ `cardinal_neighbors_only`: A `Bool` stating whether only cardinal neighbors
179
+ should be connected. By default, both cardinal _and_ diagonal neighbors are
180
+ connected. Note that when determining weights between diagonal neighbors, the
181
+ increased distance between them (as compared to the distance between cardinal
182
+ neighbors) is accounted for.
183
+
184
+ `connect_using_avg_raster_val`: `Bool`. This is intended to offer methods that
185
+ complement those used in Circuitscape.jl and Omniscape.jl. In this context,
186
+ weights (the values in `weight_raster`) are in units of electrical resistance.
187
+ If `false`, the weight between two nodes with resistances R1 and R2 is
188
+ calculated by converting resistance to conductances, taking the average, then
189
+ taking the inverse of the result to convert back to resistance:
190
+ `1 / ((1/R1 + 1/R2) / 2)`. `connect_using_avg_weights = false` correspondes to
191
+ the default settings in Circuitscape. Defaults to `true', in which case the
192
+ simple average of the weights (adjusted for distance in the case of diagonal
193
+ neighbors) in `weight_raster` are used.
194
+
195
+ `combine`: In the case that there are multiple edges defined for a single pair
196
+ of vertices, how should the weight be chosen? Defaults to `min`. See the docs
197
+ for `SparseArrays.sparse()` for more information.
198
+ """
199
+ function make_raster_graph (
90
200
weight_raster:: GeoArray ,
91
201
vertex_raster:: GeoArray ;
92
202
directed:: Bool = false ,
93
203
condition_raster:: GeoArray = weight_raster,
94
204
condition:: Function = is_data,
95
205
cardinal_neighbors_only:: Bool = false ,
96
- connect_using_avg_weights:: Bool = true
206
+ connect_using_avg_weights:: Bool = true ,
207
+ combine:: Function = min
97
208
)
98
209
# Which averaging function to use
99
210
card_avg = connect_using_avg_weights ? res_cardinal_avg : cond_cardinal_avg
100
211
diag_avg = connect_using_avg_weights ? res_diagonal_avg : cond_diagonal_avg
101
- dims = size (cost)
102
- not_no_data = cost .!= no_data_val
212
+ dims = size (weight_raster)
213
+ no_data_val = weight_raster. missingval
214
+ not_no_data = weight_raster .!= no_data_val
103
215
104
- if sum ((cost .<= 0 ) .& not_no_data) != 0
105
- @error (" cost surface contains 0 or negative values (aside " *
106
- " from the provided no_data_val ), which is not supported" )
216
+ if sum ((weight_raster .<= 0 ) .& not_no_data) != 0
217
+ @error (" weight_raster contains 0 or negative values (aside " *
218
+ " from weight_raster.missingval ), which is not supported" )
107
219
end
108
220
109
221
sources = Vector {Int64} ()
110
222
destinations = Vector {Int64} ()
111
223
node_weights = Vector {Float64} ()
112
-
224
+
113
225
# Add the edges
114
226
# Only need to do neighbors down or to the right for undirected graphs
115
227
# because edge additions will be redundant.
@@ -120,7 +232,7 @@ function make_weighted_graph(
120
232
for column in 1 : dims[2 ]
121
233
source_idx = CartesianIndex ((row, column))
122
234
if vertex_raster[source_idx] == 0 ||
123
- cost [source_idx] == weight_raster . missingval
235
+ weight_raster [source_idx] == no_data_val
124
236
continue
125
237
else
126
238
# Cardinal destination indices needed for undirected graph
@@ -269,16 +381,16 @@ function make_weighted_graph(
269
381
sources,
270
382
destinations,
271
383
node_weights,
272
- combine = min
384
+ combine = combine
273
385
)
274
386
else
275
387
g = SimpleWeightedGraph (
276
388
sources,
277
389
destinations,
278
390
node_weights,
279
- combine = min
391
+ combine = combine
280
392
)
281
393
end
282
394
283
395
return g
284
- end
396
+ end
0 commit comments