-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathgl.genleastcost.r
307 lines (266 loc) · 11.6 KB
/
gl.genleastcost.r
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#' Least-cost path analysis based on a friction matrix
#'
#' This function calculates the pairwise distances (Euclidean, cost path
#' distances and genetic distances) of populations using a friction matrix and
#' a spatial genind object. The genind object needs to have coordinates in the
#' same projected coordinate system as the friction matrix. The friction
#' matrix can be either a single raster of a stack of several layers. If a
#' stack is provided the specified cost distance is calculated for each layer
#' in the stack. The output of this function can be used with the functions
#' \code{\link[PopGenReport]{wassermann}} or
#' \code{\link[PopGenReport]{lgrMMRR}} to test for the significance of a
#' layer on the genetic structure.
#' @param x A spatial genind object. See ?popgenreport how to provide
#' coordinates in genind objects [required].
#' @param fric.raster A friction matrix [required].
#' @param gen.distance Specification which genetic distance method should be
#' used to calculate pairwise genetic distances between populations ( 'D',
#' 'Gst.Nei', 'Gst.Hedrick') or individuals ('Smouse', 'Kosman', 'propShared')
#' [required].
#' @param NN Number of neighbours used when calculating the cost distance
#' (possible values 4, 8 or 16). As the default is NULL a value has to be
#' provided if pathtype='leastcost'. NN=8 is most commonly used. Be aware that
#' linear structures may cause artefacts in the least-cost paths, therefore
#' inspect the actual least-cost paths in the provided output [default NULL].
#' @param pathtype Type of cost distance to be calculated (based on function in
#' the \code{gdistance} package. Available distances are 'leastcost', 'commute'
#' or 'rSPDistance'. See functions in the gdistance package for futher
#' explanations. If the path type is set to 'leastcost' then paths and also
#' pathlength are returned [default 'leastcost'].
#' @param plotpath switch if least cost paths should be plotted (works only if
#' pathtype='leastcost'. Be aware this slows down the computation, but it is
#' recommended to do this to check least cost paths visually.
#' @param theta value needed for rSPDistance function. See
#' \code{\link[gdistance]{rSPDistance}} in package \code{gdistance} [default 1].
#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2,
#' progress log; 3, progress and results summary; 5, full report
#' [default 2, unless specified using gl.set.verbosity].
#' @importFrom PopGenReport lgrMMRR wassermann gd.smouse gd.kosman
#' @importFrom stats step
#' @importFrom sp Line Lines SpatialLines SpatialLinesLengths
#' @importFrom raster plot
#' @return Returns a list that consists of four pairwise distance matrices
#' (Euclidean, Cost, length of path and genetic) and the actual paths as spatial
#' line objects.
#' @author Bernd Gruber (bugs? Post to
#' \url{https://groups.google.com/d/forum/dartr})
#' @seealso \code{\link{landgenreport}}, \code{\link{popgenreport}},
#' \code{\link{wassermann}}, \code{\link{lgrMMRR}}
#' @references
#' \itemize{
#' \item Cushman, S., Wasserman, T., Landguth, E. and Shirk, A. (2013).
#' Re-Evaluating Causal Modeling with Mantel Tests in Landscape Genetics.
#' Diversity, 5(1), 51-72.
#' \item Landguth, E. L., Cushman, S. A., Schwartz, M. K., McKelvey, K. S.,
#' Murphy, M. and Luikart, G. (2010). Quantifying the lag time to detect
#' barriers in landscape genetics. Molecular ecology, 4179-4191.
#' \item Wasserman, T. N., Cushman, S. A., Schwartz, M. K. and Wallin, D. O.
#' (2010). Spatial scaling and multi-model inference in landscape genetics:
#' Martes americana in northern Idaho. Landscape Ecology, 25(10), 1601-1612.
#' }
#' @examples
#' \dontrun{
#' data(possums.gl)
#' library(raster) #needed for that example
#' landscape.sim <- readRDS(system.file('extdata','landscape.sim.rdata',
#' package='dartR'))
#' glc <- gl.genleastcost(x=possums.gl,fric.raster=landscape.sim ,
#' gen.distance = 'D', NN=8, pathtype = 'leastcost',plotpath = TRUE)
#' library(PopGenReport)
#' PopGenReport::wassermann(eucl.mat = glc$eucl.mat, cost.mat = glc$cost.mats,
#' gen.mat = glc$gen.mat)
#' lgrMMRR(gen.mat = glc$gen.mat, cost.mats = glc$cost.mats,
#' eucl.mat = glc$eucl.mat)
#' }
#' @export
gl.genleastcost <- function(x,
fric.raster,
gen.distance,
NN = NULL,
pathtype = "leastcost",
plotpath = TRUE,
theta = 1,
verbose = NULL) {
# SET VERBOSITY
verbose <- gl.check.verbosity(verbose)
# FLAG SCRIPT START
funname <- match.call()[[1]]
utils.flag.start(func = funname,
build = "Jody",
verbosity = verbose)
# FUNCTION SPECIFIC ERROR CHECKING
# CHECK IF PACKAGES ARE INSTALLED
pkg <- "gdistance"
if (!(requireNamespace(pkg, quietly = TRUE))) {
stop(error(
"Package",
pkg,
" needed for this function to work. Please install it."
))
}
pkg <- "mmod"
if (!(requireNamespace(pkg, quietly = TRUE))) {
stop(error(
"Package ",
pkg,
" needed for this function to work. Please install it."
))
}
if (is.null(NN) & pathtype == "leastcost") {
stop(
error(
"NN is not specified!\nPlease specify the number of nearest neighbour to use for the least-cost path calculations (NN=4 or NN=8). If linear features are tested you may want to consider NN=4 otherwise NN=8 is the most commonly used and prefered option. In any case check the actual least-cost paths for artefacts by inspecting the plot on least-cost paths.\n"
)
)
}
dist.type <- NA
if (gen.distance == "D" ||
gen.distance == "Gst.Hedrick" ||
gen.distance == "Gst.Nei")
dist.type <- "pop"
if (gen.distance == "Kosman" ||
gen.distance == "Smouse" ||
gen.distance == "propShared")
dist.type <- "ind"
if (is.na(dist.type)) {
stop(
error(
"No valid genetic distance type was provided. Please check ?landgenreport for valid options\n"
)
)
}
if (is.null(x@other$xy)) {
cat(
warn(
"No projected coordinates in @other$xy found. Hence will use latlons (if provided), which are not projected, hence there might be distortions if the area covered is large or close to the poles. Be aware your resistance layer and coordinates in the genlight object need to have the same coordinate system.\n"
)
)
x@other$xy <- x@other$latlon[, c("lon", "lat")]
if (is.null(x@other$xy)) {
step(warn("No coordinates found in the genlight object!!\n"))
}
}
if (dist.type == "pop") {
# calculate the centers if population measurement is wanted
c.x <- tapply(x@other$xy[, 1], x@pop, mean)
c.y <- tapply(x@other$xy[, 2], x@pop, mean)
cp <- cbind(c.x, c.y)
eucl.mat <- as.matrix(dist(cp))
dimnames(eucl.mat) <- list(popNames(x), popNames(x))
npop <- length(levels(x@pop))
} else {
cp <- cbind(x@other$xy[, 1], x@other$xy[, 2])
eucl.mat <- as.matrix(dist(cp))
dimnames(eucl.mat) <- list(indNames(x), indNames(x))
npop <- length(indNames(x))
}
# check if fric.raster is a stack or not...
mats <- list()
mats.names <- NA
mats.pathlength <- list()
mats.paths <- list()
pathlength.mat <- NULL
paths <- NULL
n.mats <-
dim(fric.raster)[3] #number of rasters in the stack
for (ci in 1:n.mats) {
raster::plot(fric.raster[[ci]],
main = paste(names(fric.raster)[ci], ":", pathtype, ", NN=", NN, sep = ""))
# image(fric.raster, col=fric.raster@legend@colortable, asp=1)
points(
x@other$xy,
cex = 1,
pch = 16,
col = rainbow(nPop(x))[as.numeric(pop(x))]
)
if (dist.type == "pop")
points(cp,
cex = 1.5,
pch = 15,
col = "black")
# create friction matrix
fric.mat <-
gdistance::transition(fric.raster[[ci]], function(x)
1 / x[2], NN)
# set distances to meters if not projected already
fric.mat@crs@projargs <- "+proj=merc +units=m"
fric.mat.cor <- gdistance::geoCorrection(fric.mat)
if (pathtype == "leastcost") {
cd.mat <- gdistance::costDistance(fric.mat.cor, cp, cp)
}
if (pathtype == "rSPDistance") {
cd.mat <- gdistance::rSPDistance(fric.mat.cor, cp, cp, theta = 1)
}
if (pathtype == "commute") {
cd.mat <- as.matrix(gdistance::commuteDistance(fric.mat.cor, cp))
}
dimnames(cd.mat) <- dimnames(eucl.mat)
# only show paths if leastcost otherwise not possible
if (pathtype == "leastcost" & plotpath == TRUE) {
comb <- t(combn(1:npop, 2))
# pathlength matrix
pathlength.mat <- cd.mat
pathlength.mat[,] <- 0
paths <- list()
cols <- rainbow(dim(comb)[1], alpha = 0.5)
for (i in 1:dim(comb)[1]) {
if (dist(rbind(cp[comb[i, 1],], cp[comb[i, 2],])) == 0) {
ll <- Line(rbind(cp[comb[i, 1],], cp[comb[i, 2],]))
S1 <- Lines(list(ll), ID = "Null")
sPath <- SpatialLines(list(S1))
} else {
sPath <-
gdistance::shortestPath(fric.mat.cor, cp[comb[i, 1],], cp[comb[i, 2],], output = "SpatialLines")
}
lines(sPath, lwd = 1.5, col = cols[i])
paths[[i]] <- sPath
ll <- round(SpatialLinesLengths(sPath), 3)
pathlength.mat[comb[i, 1], comb[i, 2]] <- ll
pathlength.mat[comb[i, 2], comb[i, 1]] <- ll
}
}
mats[[ci]] <- cd.mat
mats.names[[ci]] <- names(fric.raster)[ci]
mats.pathlength[[ci]] <- pathlength.mat
mats.paths[[ci]] <- paths
} #end of ci loop
# mats[[n.mats+1]] <- eucl.mat mats.names[n.mats+1]<- 'Euclidean'
names(mats) <- names(fric.raster)
# put other calculations here.... Calculate genetic distances across
#subpopulations
xx <- gl2gi(x, verbose = 0)
if (gen.distance == "Gst.Nei") {
gendist.mat <- as.matrix(mmod::pairwise_Gst_Nei(xx))
}
if (gen.distance == "Gst.Hedrick") {
gendist.mat <- as.matrix(mmod::pairwise_Gst_Hedrick(xx))
}
if (gen.distance == "D") {
gendist.mat <- as.matrix(mmod::pairwise_D(xx))
}
if (gen.distance == "Smouse") {
gendist.mat <- as.matrix(gd.smouse(xx, verbose = FALSE))
}
if (gen.distance == "Kosman") {
gendist.mat <- as.matrix(as.dist(gd.kosman(xx)$geneticdist))
}
if (gen.distance == "propShared") {
gendist.mat <- as.matrix(as.dist(propShared(xx)))
}
dimnames(gendist.mat) <- dimnames(eucl.mat)
# FLAG SCRIPT END
if (verbose >= 1) {
cat(report("Completed:", funname, "\n"))
}
# RETURN
return(
list(
gen.mat = gendist.mat,
eucl.mat = eucl.mat,
cost.matnames = mats.names,
cost.mats = mats,
pathlength.mats = mats.pathlength,
paths = mats.paths
)
)
}