Search code examples
rrasterterra

Check if a point lies on the boundary of two raster cells


I have 25-km raster grid and a point. If I plot the point on the raster, it falls ~on the borderof two adjacent raster cells. I don't how to generate a sample data for this specific case but here's a visualisation of what I am taking about.

enter image description here

Is there any way I can detect for a group of points how many points are cases like this which could like very close to the border of two adjacent raster cells?


Solution

  • You can do that with the function on_border that I show below. Its arguments are SpatRaster r, coordinates x and y, and a tolerance for comparing real numbers.

    on_border <- function(r, x, y, tolerance = sqrt(.Machine$double.eps)) {
        v <- h <- (x >= xmin(r)) & (x <= xmax(r)) & (y >= ymin(r)) & (y <= ymax(r))
        v[v] <- ((x[v] - xmin(r)) %% res(r)[1]) < tolerance
        h[h] <- ((y[h] - ymin(r)) %% res(r)[2]) < tolerance
        h | v
    }
    

    Illustration

    library(terra)
    r <- rast(nrow=5, ncol=5, xmin=0, xmax=5, ymin=0, ymax=5, vals=1:25)
    x <- c(1, 1.5, 2, 3.5, 4.5)
    y <- c(1, 1.5, 2.5, 5, 5.2)
    
    on_border(r, x, y)
    #[1]  TRUE FALSE  TRUE  TRUE FALSE
    
    plot(r); lines(r); points(x,y, xpd=TRUE, pch=20, cex=1.5)
    

    enter image description here

    To get the distance to the nearest border

    to_border <- function(r, x, y) {
        i <- (x >= xmin(r)) & (x <= xmax(r)) & (y >= ymin(r)) & (y <= ymax(r))
        d <- rep(NA, length(i))
        d[i] <- (x[i] - xmin(r)) %% res(r)[1]
        d[i] <- pmin(d[i], (y[i] - ymin(r)) %% res(r)[2])
        d
    }
    
    to_border(r, x, y)
    #[1] 0.0 0.5 0.0 0.0  NA
    

    In principle, you could also create lines from the raster, and do a relate query like I show below, but that is really inefficient.

    pts <- vect(cbind(x, y), crs=crs(r))
    rlns <- aggregate(as.lines(r))
    relate(pts, rlns, "intersects")
    #      [,1]
    #[1,]  TRUE
    #[2,] FALSE
    #[3,]  TRUE
    #[4,]  TRUE
    #[5,] FALSE
    

    But if you wanted the distance to the border, you may need to do something like this.

    distance(pts, rlns)
    #     [,1]
    #[1,]  0.0
    #[2,]  0.5
    #[3,]  0.0
    #[4,]  0.0
    #[5,]  0.2