Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilise a function when rasterizing overlapping polygons #1041

Closed
Bartesto opened this issue Mar 1, 2023 · 5 comments
Closed

Utilise a function when rasterizing overlapping polygons #1041

Bartesto opened this issue Mar 1, 2023 · 5 comments

Comments

@Bartesto
Copy link

Bartesto commented Mar 1, 2023

Hi,

I am wondering if there is a way to apply a function to rasterization when working on overlapping polygons?

Context. I have a large multi-polygon dataset where each polygon represents the footprint of a bushfire and is attributed with the year it occurred. The dataset contains records back to 1937 for the whole of Western Australia. One task is to calculate the year since an area (or the whole State) last burnt.

I rasterize the vectors but I need to order them first by the year for this to create the correct output and the ordering imposes a time penalty when compared to something like fasterize::fasterize which can accept a function such as "max" which does the job.

Here's a toy example.

library(terra)

x1 <- rbind(c(2, 3), c(7, 3), c(7, 6), c(2, 6))
x2 <- rbind(c(3, 4), c(9, 4), c(9, 8), c(3, 8))
x3 <- rbind(c(4, 1), c(6, 1), c(6, 9), c(4, 9))
z <- rbind(cbind(object=1, part=1, x1, hole=0), cbind(object=2, part=1, x3, hole=0),
           cbind(object=3, part=1, x2, hole=0))
colnames(z)[3:4] <- c('x', 'y')
p <- vect(z, "polygons")
d <- data.frame(id = 1:3, year = c(2023, 1990, 2021))
values(p) <- d
plot(p, "year")

1stplot

What I am after can be created by:

template <- rast(p, res = 1)
rst <- p[order(p$year),] |>
  rasterize(template, field = "year")

or

library(sf)
library(raster)
library(fasterize)

sf_p <- st_as_sf(p) # fasterize works on sf class

raster_template <- raster(sf_p, res = 1) # fasterize works with raster class template

rst_fast <- fasterize(sf = sf_p, raster = raster_template, 
                             field = "year", fun = "max")

Either of these methods when plotted produce the below which has the rasterized polygons layered in descending order, with 2023 (green) on top, followed by 2021 then 1990. With multi-polygon datasets in the order of 1000's + polygons rasterize is half as fast when ordering first and I note that a function can only be applied to a point vector dataset.

2ndplot

Thanks
Bart

@rhijmans
Copy link
Member

rhijmans commented Mar 1, 2023

raster::rasterize also has that option (but is especially slow). With terra::rasterize you can do "sum=TRUE", but I will add min, max and mean (and use the "fun=" argument for that).

Here is another approach (that I would not want to use with large datasets)

u <- union(p)
tu <- t(data.frame(u))
i <- tu==0
tu[tu==1] <- p$year[tu * (1:nrow(tu))]
tu[i] <- NA
u$year <- apply(tu, 2, max, na.rm=TRUE)
template <- rast(p, res = 1)
r <- rasterize(u, template, field = "year")

@Bartesto
Copy link
Author

Bartesto commented Mar 1, 2023

Thanks @rhijmans. Really interesting to see that work around and appreciate you adding to the rasterize function. I'm weaning some folks off long running vector work in a GIS and this will go a very long way.

Regards
Bart

@Farewe
Copy link

Farewe commented Oct 3, 2023

Hello @rhijmans ,

Will there be a possibility to use a custom function to rasterize polygons in the future?

A typical example would be calculating rasters of species richness from IUCN range maps, like the good old example of raster's rasterize:
fun=function(x, ...) {length(unique(na.omit(x)))}

Switching from raster to terra in teaching practicals has made this task more complex to explain than in raster. It requires pre-processing of polygons which makes the process much slower in terra's rasterize than with raster's raterize. See for example how @damariszurell handled it by rasterizing all polygons individually here. In my own practicals I opted to union the polygons beforehand and use the sum function (an example in section #3.3 here).

rhijmans added a commit that referenced this issue Oct 5, 2023
@rhijmans
Copy link
Member

rhijmans commented Oct 5, 2023

@Farewe: you can use fun="sum" or that. I have now also added fun="count" to make that intent clearer.

library(terra)
v <- vect(system.file("ex/lux.shp", package="terra"))
v <- buffer(v, 10000)
r <- rast(system.file("ex/elev.tif", package="terra"))
x <- rasterize(v, r, fun="sum")

# now you can also do
y <- rasterize(v, r, fun="count")

@rhijmans
Copy link
Member

It is now possible to do:

rx <- rasterize(p, template, field = "year", fun="max")
rn <- rasterize(p, template, field = "year", fun="min")
rs <- rasterize(p, template, field = "year", fun="sum")
rm <- rasterize(p, template, field = "year", fun="mean")
rc <- rasterize(p, template, field = "year", fun="count")

plot(c(rx, rn, rs, rm, rc), main=c("max", "min", "sum", "mean", "count"))

rast_fun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants