Skip to content

Commit 94e4135

Browse files
committed
ELFs updates
1 parent e5bdf59 commit 94e4135

File tree

4 files changed

+146
-58
lines changed

4 files changed

+146
-58
lines changed

R/ELFs.R

Lines changed: 120 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#' ecological zones (Canadian Ecozones), are "larg-ish", more "blobby" (low fractal dimension)
2121
#' than other ecological zonation in mountains (which tend to follow elevation
2222
#' contours), and are each buffered (default 20km).
23-
#' @param nationalForestPolygon A polygon layer (`SpatVector` or `sf`) that maps the
24-
#' national forests of Canada. If missing, it will try to use
23+
#' @param x A polygon layer (`SpatVector` or `sf`) or gridded layer (`SpatRaster`)
24+
#' that maps the entire area of interest for the ELFs. If missing, it will try to use
2525
#' `scfmutils::prepInputsFireRegimePolys(type = "FRU")` if `scfmutils` is installed.
2626
#' Currently, only options with a column called `FRU` will work.
2727
#' @param desiredBuffer The distance in `m` that the buffers should be. Default is `20000`.
@@ -40,19 +40,29 @@
4040
#' and lat0 = ymin and lat1 = ymax of the individual ELF. This will create ELFs
4141
#' with the least amount of pixel deformation.
4242
#' @export
43-
makeELFs <- function(nationalForestPolygon, desiredBuffer = 20000,
43+
makeELFs <- function(x, desiredBuffer = 20000,
4444
maxArea = 2.4e+11, destinationPath = ".", singleSpatVector = FALSE,
4545
useCache = TRUE) {
46-
if (missing(nationalForestPolygon)) {
46+
if (missing(x)) {
4747
if (!requireNamespace("scfmutils")) stop("Please install PredictiveEcology/scfmutils ",
48-
"or supply a nationalForestPolygon")
49-
nationalForestPolygon <-
48+
"or supply a x")
49+
x <-
5050
scfmutils::prepInputsFireRegimePolys(type = "FRU", destinationPath = destinationPath)
5151
}
52-
digNFP <- reproducible::.robustDigest(nationalForestPolygon)
53-
dv <- terra::vect(nationalForestPolygon) |>
54-
reproducible::Cache(omitArgs = "x", .cacheExtra = digNFP,
55-
useCache = useCache)
52+
digNFP <- reproducible::.robustDigest(x)
53+
if (is(x, "sf")) {
54+
dv <- terra::vect(x) |>
55+
reproducible::Cache(omitArgs = "x", .cacheExtra = digNFP,
56+
useCache = useCache)
57+
} else if (is(x, "SpatRaster")) {
58+
dv <- {
59+
`>=`(x, 0) |>
60+
terra::as.polygons() |>
61+
terra::buffer(4*res(x)[1]) |>
62+
terra::buffer(-4*res(x)[1]) } |>
63+
Cache(.cacheExtra = digNFP, omitArgs = "x",
64+
.functionName = "Create polygon from input SpatRaster")
65+
}
5666
ecoNames <- c(
5767
"zone", "region",
5868
"province"
@@ -66,16 +76,23 @@ makeELFs <- function(nationalForestPolygon, desiredBuffer = 20000,
6676
ecosLCC <- Map(nam = ecoNames, url = urls, function(nam, url) {
6777
reproducible::prepInputs(url = url,
6878
destinationPath = destinationPath,
69-
projectTo = nationalForestPolygon,
79+
projectTo = x,
7080
useCache = useCache) |>
7181
reproducible::Cache(omitArgs = "projectTo",
72-
.cacheExtra = list(nationalForestPolygon = digNFP),
73-
useCache = useCache)
82+
.cacheExtra = list(x = digNFP),
83+
useCache = useCache, .functionName = paste0("prepInputs_", nam))
7484
})
7585
tmpl <- terra::rast(ecosLCC[[1]], res = 5000)
76-
hf <- terra::rasterize(nationalForestPolygon, tmpl, field = "FRU") |>
77-
reproducible::Cache(omitArgs = "x", .cacheExtra = digNFP,
78-
useCache = useCache)
86+
if (is(x, "SpatRaster")) {
87+
hf <- terra::project(x, tmpl)
88+
} else {
89+
hf <- terra::rasterize(x, tmpl, field = "FRU") |>
90+
reproducible::Cache(omitArgs = "x", .cacheExtra = digNFP,
91+
useCache = useCache)
92+
}
93+
94+
hf <- terra::trim(hf)
95+
7996
ecosR <- Map(ecoLCC = ecosLCC, nam = names(ecosLCC),
8097
function(ecoLCC, nam) {
8198
terra::rasterize(ecoLCC, tmpl, field = toupper(paste0("eco", substr(nam, 1, 7)))) |>
@@ -97,27 +114,37 @@ makeELFs <- function(nationalForestPolygon, desiredBuffer = 20000,
97114

98115
# plotStack(ecopRseg)
99116
message("Starting to buffer ELFs")
100-
out <- bufferOut(dv, desiredBuffer = desiredBuffer,
101-
useCache = useCache) |>
102-
reproducible::Cache(omitArgs = "v", .cacheExtra = digNFP,
103-
useCache = useCache)
117+
# out <- bufferOut(dv, desiredBuffer = desiredBuffer, # spatRas = x,
118+
# useCache = useCache) |>
119+
# reproducible::Cache(omitArgs = "v", .cacheExtra = digNFP,
120+
# useCache = useCache)
104121
out2 <- mergeAndSplitRas(ecopRseg, ecosLCC$province, maxArea = maxArea,
105-
useCache = useCache) |>
106-
Cache(useCache = useCache)
122+
useCache = useCache, destinationPath = destinationPath) |>
123+
reproducible::Cache(omitArgs = "ecopRseg", .cacheExtra = attr(ecosR, "tags"))
107124

108125
out3 <- lapply(out2, function(x) as.list(segregateKeepNames(x, omitClasses = 0))) |>
109126
unlist(recursive = FALSE)
110127
names(out3) <- sapply(out3, names)
111128

112-
ELFs <- bufferOut(spatRasSeg = out3, mask = out$rasWhole[[1]],
129+
ELFs <- bufferOut(spatRasSeg = out3, mask = hf,
113130
desiredBuffer = desiredBuffer,
114131
useCache = useCache) |>
115132
reproducible::Cache(omitArgs = c("spatRasSeg", "mask"),
116-
.cacheExtra = list(nationalForestPolygon = digNFP,
117-
attr(out2, "tags"),
118-
attr(out, "tags")
133+
.cacheExtra = list(x = digNFP,
134+
attr(out2, "tags")#,
135+
#attr(out, "tags")
119136
),
120137
useCache = useCache)
138+
139+
# ELFs <- bufferOut(spatRasSeg = out3, mask = out$rasWhole[[1]],
140+
# desiredBuffer = desiredBuffer,
141+
# useCache = useCache) |>
142+
# reproducible::Cache(omitArgs = c("spatRasSeg", "mask"),
143+
# .cacheExtra = list(x = digNFP,
144+
# attr(out2, "tags"),
145+
# attr(out, "tags")
146+
# ),
147+
# useCache = useCache)
121148
if (isTRUE(singleSpatVector)) {
122149

123150
ELFs <- makeELFs(destinationPath = "inputs")
@@ -152,6 +179,8 @@ bufferOut <- function(v, spatRasSeg, spatRas, mask, field = "FRU", desiredBuffer
152179
terra::rasterize(v, y = _, field = "FRU", touches = TRUE)} |>
153180
reproducible::Cache(omitArgs = "x",
154181
useCache = useCache)
182+
}
183+
if (missing(spatRasSeg)) {
155184
spatRasSeg <- terra::segregate(spatRas)
156185
}
157186

@@ -237,37 +266,53 @@ bufferOut <- function(v, spatRasSeg, spatRas, mask, field = "FRU", desiredBuffer
237266

238267
# need to mask 2x maybe
239268
r[[i]] <- reproducible::maskTo(r[[i]], mask, verbose = FALSE, touches = FALSE)
240-
ca[[i]] <- reproducible::maskTo(ca[[i]], mask, verbose = FALSE, touches = FALSE)
241-
242-
patchs <- terra::patches(r[[i]], allowGaps = FALSE, values = FALSE, directions = 8)
243-
tab <- terra::freq(patchs)
244-
245-
if (NROW(tab) > 1) {
246-
tooSmall <- tab$count < 500
247-
if (any(tooSmall)) {
248-
249-
r[[i]][patchs[] %in% tab$value[tooSmall]] <- NA
250-
r[[i]] <- terra::trim(r[[i]])
251-
a <- ca[[i]]
252-
a[ca[[i]] == 0] <- NA
253-
patchsCA <- terra::patches(a, allowGaps = FALSE, values = FALSE, directions = 8)
254-
# patchsCA <- terra::patches(ca[[i]] > 0, allowGaps = FALSE, values = FALSE, directions = 8)
255-
tab <- terra::freq(patchsCA)
256-
tooSmall <- tab$count < 500
257-
wh <- which(patchsCA[] %in% tab$value[tooSmall])
258-
dtLost <- data.table(pixelID = wh, value = terra::values(ca[[i]])[wh])
269+
if (NROW(terra::freq(is.na(r[[i]]))) > 1) {
270+
ca[[i]] <- reproducible::maskTo(ca[[i]], mask, verbose = FALSE, touches = FALSE)
259271

260-
lostPixels[[i]] <- dtLost
261-
ca[[i]][wh] <- 0
272+
patchs <- terra::patches(r[[i]], allowGaps = FALSE, values = FALSE, directions = 8)
273+
tab <- terra::freq(patchs)
274+
275+
if (NROW(tab) > 1) {
276+
tooSmall <- tab$count < 500
277+
if (!all(tooSmall)) {
278+
if (any(tooSmall)) {
279+
280+
r[[i]][patchs[] %in% tab$value[tooSmall]] <- NA
281+
r[[i]] <- terra::trim(r[[i]])
282+
a <- ca[[i]]
283+
a[ca[[i]] == 0] <- NA
284+
patchsCA <- terra::patches(a, allowGaps = FALSE, values = FALSE, directions = 8)
285+
# patchsCA <- terra::patches(ca[[i]] > 0, allowGaps = FALSE, values = FALSE, directions = 8)
286+
tab <- terra::freq(patchsCA)
287+
tooSmall <- tab$count < 500
288+
wh <- which(patchsCA[] %in% tab$value[tooSmall])
289+
dtLost <- data.table(pixelID = wh, value = terra::values(ca[[i]])[wh])
290+
291+
lostPixels[[i]] <- dtLost
292+
ca[[i]][wh] <- 0
293+
}
294+
}
262295
}
296+
# r[[i]] <- maskTo(r[[i]], mask, verbose = FALSE, touches = FALSE)
297+
# ca[[i]] <- maskTo(ca[[i]], mask, verbose = FALSE, touches = FALSE)
263298
}
264-
# r[[i]] <- maskTo(r[[i]], mask, verbose = FALSE, touches = FALSE)
265-
# ca[[i]] <- maskTo(ca[[i]], mask, verbose = FALSE, touches = FALSE)
266-
267299
print(paste0("Done ", i))
268300
}
269301

270302
ll <- moveSliversToOtherELFs(lostPixels, lp, ca, i, r)
303+
ll3 <- ll
304+
destinationPath <- unique(dirname(Filenames(spatRasSeg)))
305+
ELFpath <- file.path(destinationPath, "ELFs_final")
306+
unlink(ELFpath, recursive = TRUE)
307+
dir.create(ELFpath, recursive = TRUE, showWarnings = FALSE)
308+
message("Writing ELF rasters to disk")
309+
for (i in seq_along(ll)) {
310+
for (j in seq_along(i)) {
311+
fn <- file.path(ELFpath, paste0(names(ll[[i]][[j]]), ".tif"))
312+
unlink(fn)
313+
ll[[i]][[j]] <- terra::writeRaster(ll[[i]][[j]], filename = fn, overwrite = TRUE)
314+
}
315+
}
271316

272317
list(rasCentered = ll$r, rasWhole = ll$ca)
273318
}
@@ -278,6 +323,7 @@ segregateKeepNames <- function(ecopR, omitClasses, classes = NULL) {
278323
classes <- setdiff(uniquVals, omitClasses)
279324
}
280325
if (length(setdiff(uniquVals, 0)) > 1) {
326+
origFilenames <- Filenames(ecopR)
281327
ecopRseg <- terra::segregate(ecopR, classes = classes)
282328
if (is.factor(ecopR)) {
283329
names(ecopRseg) <- cats(ecopR)[[1]][match(as.numeric(names(ecopRseg)), cats(ecopR)[[1]]$ID), 2]
@@ -291,6 +337,10 @@ segregateKeepNames <- function(ecopR, omitClasses, classes = NULL) {
291337
data.table::setorder(dt, "num")
292338
ecopR <- ecopRseg[[match(dt$nam, names(ecopRseg))]]
293339
}
340+
if (terra::inMemory(ecopR))
341+
ecopR <- writeRaster(ecopR, overwrite = TRUE,
342+
filename = file.path(dirname(origFilenames),
343+
paste0(paste(names(ecopR), collapse = "_"), ".tif")))
294344
ecopR
295345

296346
}
@@ -323,7 +373,8 @@ split_poly <- function(sf_poly, n_areas) {
323373
}
324374

325375
mergeAndSplitRas <- function(ecopRseg, ecopLCC, maxArea = 2.4e+11,
326-
field = "ECOPROVINC", useCache = TRUE) { # FRU 27 ... or FRU 26 is 4.02075e+11
376+
field = "ECOPROVINC", useCache = TRUE,
377+
destinationPath) { # FRU 27 ... or FRU 26 is 4.02075e+11
327378
# Require::Require(c(sf, dismo))
328379
prov1 <- as.character(sort(as.numeric(unique(names(ecopRseg)))))
329380
out <- Map(prov = prov1, function(prov) {
@@ -356,11 +407,15 @@ mergeAndSplitRas <- function(ecopRseg, ecopLCC, maxArea = 2.4e+11,
356407
tmpRas <- try(terra::rasterize(tmp, ecopRseg[[provChar]], field = field, touches = TRUE))
357408
whChange <- tmpRas[] > 0
358409
a <- ecopRseg[[provChar]]
410+
# Next line doesn't work --> it pulls it into memory
411+
# set.values(a, cells = which(whChange %in% 1), values(tmpRas, mat = FALSE)[whChange %in% 1])
359412
a[whChange] <- tmpRas[whChange]
360-
tmp <- a
361413
} else {
362-
tmp <- ecopRseg[[provChar]]
414+
a <- ecopRseg[[provChar]]
363415
}
416+
# Need to write it explicitly because it keeps the original "fullRaster" as its .tif
417+
tmp <- writeRaster(a, filename = file.path(destinationPath, paste0(provChar, ".tif")),
418+
overwrite = TRUE)
364419
message("Done ", prov)
365420
tmp
366421
})
@@ -401,11 +456,20 @@ moveSliversToOtherELFs <- function(lostPixels, lp, ca, i, r) {
401456
sums <- list()
402457
for (addToInd in addTo) {
403458
curPixelVal <- ca[[addToInd]][] != 2
404-
a[[addToInd]] <- ca[[addToInd]]
459+
arr <- try(a[[addToInd]] <- ca[[addToInd]])
460+
if (is(arr, "try-error")) browser()
405461
a[[addToInd]][lostPixels[[lp]]$pixelID] <- pmax(terra::values(a[[addToInd]])[lostPixels[[lp]]$pixelID], lostPixels[[lp]]$value, na.rm = TRUE)
406462
theA <- terra::freq(a[[addToInd]])
407463
# theA <- lapply(a, function(x) if (!is.null(x)) terra::freq(x))
408-
sums[[addToInd]] <- sum(theA[-1,] - terra::freq(ca[[addToInd]])[-1,])
464+
ar1 <- theA[-1, ]
465+
br1 <- terra::freq(ca[[addToInd]])[-1,]
466+
if (NROW(ar1) != NROW(br1)) {
467+
missings <- setdiff(br1$value, ar1$value)
468+
a2 <- data.frame(layer = ar1$layer[1], value = missings, count = br1$count[br1$value %in% missings])
469+
ar1 <- rbind(ar1, a2)
470+
ar1 <- ar1[order(ar1$value),]
471+
}
472+
sums[[addToInd]] <- sum(ar1 - br1)
409473
}
410474
addTo <- which.min(unlist(lapply(sums, function(ss) if (is.null(ss)) 1e7 else ss )))
411475
if (hasNames)
@@ -420,6 +484,7 @@ moveSliversToOtherELFs <- function(lostPixels, lp, ca, i, r) {
420484
a[lostPixels[[lp]]$pixelID] <- newVals
421485
# a[a[] == 0] <- NA
422486
a <- terra::trim(a)
487+
# if (is(a, "try-error")) browser()
423488
a <- terra::project(a, terra::crs(r[[addTo]]), method = "near")
424489
bb <- terra::resample(a, r[[addTo]], method = "near")
425490
whVals <- which(terra::values(bb) > 0)

R/fireSenseCloudParameterStatus.R

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ fireSenseCloudParametersMap <-
6060
oo
6161
}
6262

63+
#' @export
64+
#' @rdname makeELFs
65+
#' @seealso [fireSenseCloudParameters()]
66+
plotELFs <- function(destinationPath = ".") {
67+
ELFs <- makeELFs(destinationPath = destinationPath, singleSpatVector = TRUE) |>
68+
Cache()
69+
ELFs2 <- makeELFs(destinationPath = destinationPath, singleSpatVector = FALSE)|>
70+
Cache()
71+
72+
# terra::plot(ELFs, col = c("turquoise", "yellow")[ELFs$buffer], alpha = 0.5)
73+
terra::plot(ELFs)
74+
keep <- ELFs$buffer %in% 2
75+
terra::text(terra::centroids(ELFs[ keep, ]), labels = ELFs$ID[keep], cex = 0.7)
76+
return(invisible(ELFs))
77+
}
6378

6479

6580
#' Get current state of FireSense Fit parameters as a map
@@ -75,9 +90,11 @@ fireSenseCloudParametersMap <-
7590
#' @return The object and the `rds` file saved to destinationPath.
7691
#' @export
7792
#' @seealso [fireSenseCloudParametersMap()]
78-
fireSenseCloudParameters <- function(url = "https://drive.google.com/file/d/1xQGAhBCRimYQC_GWA0lOctZU4VSlyojS/view?usp=drivesdk",
93+
fireSenseCloudParameters <- function(url = "https://drive.google.com/file/d/1oJX7V5wPSj49C6wt59yD9RBplfZ7Cp-f/view?usp=drive_link",
7994
targetFile = "fireSenseParams.rds",
8095
destinationPath = ".", useCache = TRUE) {
96+
97+
# KNN = "https://drive.google.com/file/d/1xQGAhBCRimYQC_GWA0lOctZU4VSlyojS/view?usp=drivesdk"
8198
prepInputs(targetFile = targetFile,
8299
url = url,
83100
destinationPath = destinationPath,

man/fireSenseCloudParameters.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/makeELFs.Rd

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)