I have two sf objects with multiple polygon features. The two objects have attributes that I would like to join to each other using the polygons. I have managed to do so with st_join(), but not in the way that I expected.
When using st_join(), I initially expected that if I specified the argument st_join(x, y, left = TRUE), that the resulting object would have the same number of features as x if all the geometries in x are contained in y. For example:
library("sf")
library("tidyverse")
map1 <- st_as_sfc(c("POLYGON((0 0 , 0 1 , -1 1 , -1 0, 0 0))",
"POLYGON((0 0 , 0 2 , 2 2 , 2 0, 0 0 ))",
"POLYGON((0 0 , 0 -0.5 , -0.5 -0.5 , -0.5 0, 0 0))")) %>%
st_sf(ID = paste0("poly", 1:3),
att1 = c(letters[1:3]))
map2 <- st_as_sfc(c("POLYGON((0 0 , 0 0.5 , -0.5 .5 , -0.5 0, 0 0))",
"POLYGON((-0.5 0 , -0.5 1 , -1 1 , -1 0 , -0.5 0))",
"POLYGON((0 0 , 0 1 , 1 1 , 1 0 , 0 0))",
"POLYGON((1 1 , 1 2 , 2 2 , 2 1 , 1 1))",
"POLYGON((0 0 , 0 -0.25 , -0.25 -0.25 , -0.25 0 , 0 0))")) %>%
st_sf(ID = paste0("poly", 4:8)) %>%
mutate(att2 = c(LETTERS[1:5]))
map3 <- st_join(map2,
map1,
left = TRUE)
My expectation would be that map3 would have 5 features because all the polygons in map2 are contained uniquely in features of map1. This is however not the case, and polygons that do not overlap appear to have been joined with each other:
Simple feature collection with 12 features and 4 fields
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: -1 ymin: -0.25 xmax: 2 ymax: 2
CRS: NA
First 10 features:
ID.x att2 ID.y att1 .
1 poly4 A poly1 a POLYGON ((0 0, 0 0.5, -0.5 ...
1.1 poly4 A poly2 b POLYGON ((0 0, 0 0.5, -0.5 ...
1.2 poly4 A poly3 c POLYGON ((0 0, 0 0.5, -0.5 ...
2 poly5 B poly1 a POLYGON ((-0.5 0, -0.5 1, -...
2.1 poly5 B poly3 c POLYGON ((-0.5 0, -0.5 1, -...
3 poly6 C poly1 a POLYGON ((0 0, 0 1, 1 1, 1 ...
3.1 poly6 C poly2 b POLYGON ((0 0, 0 1, 1 1, 1 ...
3.2 poly6 C poly3 c POLYGON ((0 0, 0 1, 1 1, 1 ...
4 poly7 D poly2 b POLYGON ((1 1, 1 2, 2 2, 2 ...
5 poly8 E poly1 a POLYGON ((0 0, 0 -0.25, -0....
I have managed to get the expected result by using the argument largest = TRUE as follows:
map4 <- st_join(map2,
map1,
left = TRUE,
largest = TRUE)
I however don't understand why this gives me the desired output but omitting largest = TRUE does not.
Thanks for taking the time to help!
Default predicate for st_join() is st_intersects, which is also TRUE for touching geometries.
intersects- A and B are not disjoint
disjoint- A and B have no points in common
( https://r-spatial.org/book/03-Geometries.html#sec-de9im )
In your case, you seem to be after st_within.
within- None of the points of B are outside A
Lets first visualize how those features relate:
library("sf")
#> Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; sf_use_s2() is TRUE
library("tidyverse")
map1 <- st_as_sfc(c("POLYGON((0 0 , 0 1 , -1 1 , -1 0, 0 0))",
"POLYGON((0 0 , 0 2 , 2 2 , 2 0, 0 0 ))",
"POLYGON((0 0 , 0 -0.5 , -0.5 -0.5 , -0.5 0, 0 0))")) %>%
st_sf(ID = paste0("poly", 1:3),
att1 = c(letters[1:3]))
map2 <- st_as_sfc(c("POLYGON((0 0 , 0 0.5 , -0.5 .5 , -0.5 0, 0 0))",
"POLYGON((-0.5 0 , -0.5 1 , -1 1 , -1 0 , -0.5 0))",
"POLYGON((0 0 , 0 1 , 1 1 , 1 0 , 0 0))",
"POLYGON((1 1 , 1 2 , 2 2 , 2 1 , 1 1))",
"POLYGON((0 0 , 0 -0.25 , -0.25 -0.25 , -0.25 0 , 0 0))")) %>%
st_sf(ID = paste0("poly", 4:8)) %>%
mutate(att2 = c(LETTERS[1:5]))
ggplot() +
geom_sf(data = map1, aes(fill = "map1"), linewidth = 5,) +
geom_sf(data = map2, aes(fill = "map2"), color = "white", , linewidth = 1, alpha = .2) +
geom_sf_label(data = map1, aes(label = ID, fill = "map1")) +
geom_sf_label(data = map2, aes(label = ID, fill = "map2"), color = "white") +
theme_minimal()

With st_join(... , join = st_intersects) (default):
st_join(map2, map1, left = TRUE, join = st_intersects)
#> Simple feature collection with 12 features and 4 fields
#> Geometry type: POLYGON
#> Dimension: XY
#> Bounding box: xmin: -1 ymin: -0.25 xmax: 2 ymax: 2
#> CRS: NA
#> First 10 features:
#> ID.x att2 ID.y att1 .
#> 1 poly4 A poly1 a POLYGON ((0 0, 0 0.5, -0.5 ...
#> 1.1 poly4 A poly2 b POLYGON ((0 0, 0 0.5, -0.5 ...
#> 1.2 poly4 A poly3 c POLYGON ((0 0, 0 0.5, -0.5 ...
#> 2 poly5 B poly1 a POLYGON ((-0.5 0, -0.5 1, -...
#> 2.1 poly5 B poly3 c POLYGON ((-0.5 0, -0.5 1, -...
#> 3 poly6 C poly1 a POLYGON ((0 0, 0 1, 1 1, 1 ...
#> 3.1 poly6 C poly2 b POLYGON ((0 0, 0 1, 1 1, 1 ...
#> 3.2 poly6 C poly3 c POLYGON ((0 0, 0 1, 1 1, 1 ...
#> 4 poly7 D poly2 b POLYGON ((1 1, 1 2, 2 2, 2 ...
#> 5 poly8 E poly1 a POLYGON ((0 0, 0 -0.25, -0....
With st_join(... , join = st_within):
st_join(map2, map1, left = TRUE, join = st_within)
#> Simple feature collection with 5 features and 4 fields
#> Geometry type: POLYGON
#> Dimension: XY
#> Bounding box: xmin: -1 ymin: -0.25 xmax: 2 ymax: 2
#> CRS: NA
#> ID.x att2 ID.y att1 .
#> 1 poly4 A poly1 a POLYGON ((0 0, 0 0.5, -0.5 ...
#> 2 poly5 B poly1 a POLYGON ((-0.5 0, -0.5 1, -...
#> 3 poly6 C poly2 b POLYGON ((0 0, 0 1, 1 1, 1 ...
#> 4 poly7 D poly2 b POLYGON ((1 1, 1 2, 2 2, 2 ...
#> 5 poly8 E poly3 c POLYGON ((0 0, 0 -0.25, -0....
Created on 2024-04-15 with reprex v2.1.0
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