Im trying to use the sf package in R to see if sf object is within another sf object with the st_within
function. My issue is with the output of this function which is sparse geometry binary predicate - sgbp
and I need a vector as an output so that I can use the dplyr
package afterwards for filtering. Here is a simplified example:
# object 1: I will test if it is inside object 2
df <- data.frame(lon = c(2.5, 3, 3.5), lat = c(2.5, 3, 3.5), var = 1) %>%
st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4326) %>%
summarise(var = sum(var), do_union = F) %>% st_cast("LINESTRING")
# object 2: I will test if it contains object 1
box <- data.frame(lon = c(2, 4, 4, 2, 2), lat = c(2, 2, 4, 4,2), var = 1) %>%
st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4326) %>%
summarise(var = sum(var), do_union = F) %>% st_cast("POLYGON")
# test 1
df$indicator <- st_within(df$geometry, box$geometry) # gives geometric binary predicate on pairs of sf sets which cannot be used
df <- df %>% filter(indicator == 1)
This gives Error: Column indicator
must be a 1d atomic vector or a list.
I tried solving this problem below:
# test 2
df$indicator <- st_within(df$geometry, box$geometry, sparse = F) %>%
diag() # gives matrix that I convert with diag() into vector
df <- df %>% filter(indicator == FALSE)
This works, it removes the row that contains TRUE values but the process of making a matrix is very slow for my calculations since my real data contains many observations. Is there a way to make the output of st_within
a character vector, or maybe a way to convert sgbp
to a character vector compatible with dplyr
without making a matrix?
sf: Simple Features for R Support for simple features, a standardized way to encode spatial vector data. Binds to 'GDAL' for reading and writing data, to 'GEOS' for geometrical operations, and to 'PROJ' for projection conversions and datum transformations.
sf: objects with simple features frame row, consisting of attributes and geometry. in blue a single simple feature geometry (an object of class sfg ) in red a simple feature list-column (an object of class sfc , which is a column in the data. frame )
1 Solarflarecoin is 0.000127 Polygon. So, you've converted 1 Solarflarecoin to 0.000127 Polygon. We used 7904.162 International Currency Exchange Rate.
Here is how you can get a logical vector from sparse geometry binary predicate:
df$indicator <- st_within(df, box) %>% lengths > 0
or to subset without creating a new variable:
df <- df[st_within(df, box) %>% lengths > 0,]
I cannot test on your large dataset unfortunately but please let me know if it is faster than matrix approach.
The result of is_within
is in truth a list column, so you can work your
way out of this by "unlisting" it. Something like this would work:
library(dplyr)
library(sf)
# object 1: I will test if it is inside object 2 - to make this more interesting
# I added a second not-contained line
df <- data.frame(lon = c(2.5, 3, 3.5), lat = c(2.5, 3, 3.5), var = 1) %>%
st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4326) %>%
summarise(var = sum(var), do_union = F) %>% st_cast("LINESTRING")
df2 <- data.frame(lon = c(4.5, 5, 6), lat = c(4.5, 5, 6), var = 2) %>%
st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4326) %>%
summarise(var = sum(var), do_union = F) %>% st_cast("LINESTRING")
df3 <- rbind(df, df2)
# object 2: I will test if it contains object 1
box <- data.frame(lon = c(2, 4, 4, 2, 2), lat = c(2, 2, 4, 4,2), var = 1) %>%
st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4326) %>%
summarise(var = sum(var), do_union = F) %>% st_cast("POLYGON")
plot(df3)
plot(st_geometry(box), add = TRUE)
# see if the lines are within the box and build a data frame with results
is_within <- st_within(df3$geometry, box$geometry) %>%
lapply(FUN = function(x) data.frame(ind = length(x))) %>%
bind_rows()
# add the "indicator" to df3
df3 <- dplyr::mutate(df3, indicator = is_within$ind)
df3
#> Simple feature collection with 2 features and 2 fields
#> geometry type: LINESTRING
#> dimension: XY
#> bbox: xmin: 2.5 ymin: 2.5 xmax: 6 ymax: 6
#> epsg (SRID): 4326
#> proj4string: +proj=longlat +datum=WGS84 +no_defs
#> var indicator geometry
#> 1 3 1 LINESTRING (2.5 2.5, 3 3, 3...
#> 2 6 0 LINESTRING (4.5 4.5, 5 5, 6 6)
HTH
Created on 2018-03-15 by the reprex package (v0.2.0).
Instead of using the st_within
function directly, try using a spatial join
.
Check out the following example how st_joins works
library(sf)
library(tidyverse)
lines <-
data.frame(id=gl(3,2), x=c(-3,2,6,11,7,10), y=c(-1,6,-5,-9,10,5)) %>%
st_as_sf(coords=c("x","y"), remove=F) %>%
group_by(id) %>%
summarise() %>%
st_cast("LINESTRING")
yta10 <-
st_point(c(0, 0)) %>%
st_buffer(dist = 10) %>%
st_sfc() %>%
st_sf(yta = "10m")
With a left join all lines are kept, but you can see which of them that are located inside the polygon
lines %>% st_join(yta10, left=TRUE)
An inner join (left = FALSE) only keeps the ones inside
lines %>% st_join(yta10, left=FALSE)
The latter can also be obtained by
lines[yta10,]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With