Search code examples
rspatialr-sfspatial-query

Simple st_intersect example gives unexpected result


I'm using the sf package in R to simulate a sample of agents moving between different nodes in a network across space-time.

I'm currently puzzled though by some behavior from st_intersects: I have the agents moving between nodes between each corner of the coordinate unit square as well as through the center at (.5,.5). However when I try to detect an agent at st_point(c(.1,.9)) intersecting with the geometry st_linestring(c(st_point(c(0,0)),st_point(c(0.5,0.5)))) I get an empty predicate return.

In contrast if I detect an agent moving along the x-axis or y-axis only, I am able to detect the point correctly. Why is this?

Minimum reproducible example in R v4.0.2:

library(sf)

l1 <- st_linestring(c(st_point(c(0,1)),st_point(c(0.5,0.5))))
p1 <- st_point(c(.1,.9)) ## on the line between (0,1) and (.5,.5); y=1-x x = f(t)


st_intersects(p1,l1) ## empty
#Sparse geometry binary predicate list of length 1, where the predicate was `intersects'
# 1: (empty)


## in contrast
l2 <- st_linestring(st_point(c(0,0)),st_point(c(1,0)))
p2 <- st_point(c(.1,0)) ## on the line between (0,0) and (1,0) ; y = 0; x = f(t)

st_intersects(p2,l2) ## returns 1 as I would expect
#Sparse geometry binary predicate list of length 1, where the predicate was `intersects'
# 1: 1

Solution

  • To elaborate a bit on the ege-rubak's answer: The point is off by tiny difference, most likely due to floating point math (which is inherently inaccurate).

    As a workaround I suggest using sf::st_is_within_distance() with a sufficiently small dist value to eliminate rounding differences without introducing false positives / may require some tuning depending on the data used.

    Consider this code, originally posted on the RStudio Community forum (where this question seems to have been cross posted): https://community.rstudio.com/t/simple-st-intersect-gives-unexpected-result/108214/3?u=jlacko

    library(sf)
    l1 <- st_linestring(c(st_point(c(0,1)),
                          st_point(c(0.5,0.5))))
    p1 <- st_point(c(.1,.9)) ## on the line between (0,1) and (.5,.5); y=1-x x = f(t)
    
    st_distance(l1, p1)[1,1]
    # [1] 1.962616e-17
    
    st_is_within_distance(p1,l1, 1/1000) 
    # Sparse geometry binary predicate list of length 1, where the
    # predicate was `is_within_distance'
    # 1: 1