rgeospatialterra

terra::extract exact=TRUE error TopologyException


I was playing around comparing functionalities from terra::extract and exactextractr ::exact_extract. I noticed that for terra::extract, exact = TRUE sometimes do not work, but weight=TRUE does.

  1. When would it be better to use exact vs weight?
  2. Is there a bug in extract with the argument exact=TRUE?
  3. Is it possible to get the proportion of pixel overlap from the polygon like in exact_extract(rast, poly), but for terra::extract?
library(sf)
library(terra)
library(exactextractr)

rt=list(test1= matrix(1:100, ncol=10), 
     test2= matrix(201:300, ncol=10))

rast1 <- terra::rast((rt$test1))
rast2 <- terra::rast((rt$test1+2))

rast = c(rast1, rast2)

val1 = 0
val2 = 1.50
vac1= c(val1, val1, val2, val1, val2, val2, val1, val2, val1, val1)
vm1 = matrix(vac1, ncol = 2, byrow = TRUE)
pol1 = st_polygon(list(vm1))
pol2 = st_polygon(list(vm1+2))
pol3= st_polygon(list(vm1+4, vm1+6))
poly = st_sfc(pol1, pol2, pol3) %>% st_as_sf()

names(rast) <- c('t1', 't2')
rast_m=mask(rast, poly)

pt.pix = xyFromCell(rast, 1:100)


plot(rast_m[[1]]);plot(poly, add = TRUE)
plot(st_multipoint(pt.pix), add= TRUE, pch = ".", cex = 5)
text(st_multipoint(pt.pix+.15), labels = values(rast[[1]]))

values(rast_m[[1]]) %>% mean(., na.rm = T)

terra::extract(x = rast, y = poly, 'sum', exact = FALSE)


terra::extract(x = rast, y = poly, 'sum', exact = TRUE)

Gives the

Error: TopologyException: Input geom 1 is invalid: Hole lies outside shell at 6 6

But with weight, it works...

terra::extract(x = rast, y = poly, 'mean', weight = TRUE)
# (10+10+4.5+19/4)/(1.5^2)
exact_extract(rast, poly, 'mean')
e = exact_extract(rast, poly)

enter image description here


Solution

  • That's because the poly geometry is not valid:

    sf::st_is_valid(poly)
    # [1]  TRUE  TRUE FALSE
    
    poly <- sf::st_make_valid(poly)
    terra::extract(x = rast, y = poly, 'sum', exact = TRUE)
    #   ID     t1     t2
    # 1  1  29.25  33.75
    # 2  2  69.75  74.25
    # 3  3 261.00 270.00
    
    

    Ad 3:

    exact_extract(rast, poly)
    #> [[1]]
    #>   t1 t2 coverage_fraction
    #> 1  9 11              0.50
    #> 2 19 21              0.25
    #> 3 10 12              1.00
    #> 4 20 22              0.50
    #> 
    #> [[2]]
    #>   t1 t2 coverage_fraction
    #> 1 27 29              0.50
    #> 2 37 39              0.25
    #> 3 28 30              1.00
    #> 4 38 40              0.50
    #> 
    #> [[3]]
    #>   t1 t2 coverage_fraction
    #> 1 63 65              0.50
    #> 2 73 75              0.25
    #> 3 64 66              1.00
    #> 4 74 76              0.50
    #> 5 45 47              0.50
    #> 6 55 57              0.25
    #> 7 46 48              1.00
    #> 8 56 58              0.50
    
    terra::extract(rast, poly, weights = TRUE)
    #>    ID t1 t2 weight
    #> 1   1  9 11   0.50
    #> 2   1 19 21   0.25
    #> 3   1 10 12   1.00
    #> 4   1 20 22   0.50
    #> 5   2 27 29   0.50
    #> 6   2 37 39   0.25
    #> 7   2 28 30   1.00
    #> 8   2 38 40   0.50
    #> 9   3 63 65   0.50
    #> 10  3 73 75   0.25
    #> 11  3 64 66   1.00
    #> 12  3 74 76   0.50
    #> 13  3 45 47   0.50
    #> 14  3 55 57   0.25
    #> 15  3 46 48   1.00
    #> 16  3 56 58   0.50
    

    Created on 2024-10-25 with reprex v2.1.0