To the st_cast team @mdsumner and @etiennebr : (how) can we select e.g. the one polygon feature geometry created from:
> g = st_makegrid(n=c(2,2), offset = c(0,0), cellsize = c(2,2))
> s = st_sfc(st_polygon(list(rbind(c(1,1), c(2,1),c(2,2),c(1,2),c(1,1)))))
> i = st_intersection(st_sf(a=1:4, geom = g), st_sf(b = 2, geom = s))
Warning message:
In st_intersection(st_sf(a = 1:4, geom = g), st_sf(b = 2, geom = s)) :
attribute variables are assumed to be spatially constant throughout all geometries
> i
Simple feature collection with 4 features and 2 fields
geometry type: GEOMETRY
dimension: XY
bbox: xmin: 1 ymin: 1 xmax: 2 ymax: 2
epsg (SRID): NA
proj4string: NA
a b geometry
1 1 2 POLYGON((2 2, 2 1, 1 1, 1 2...
2 2 2 LINESTRING(2 2, 2 1)
3 3 2 LINESTRING(1 2, 2 2)
4 4 2 POINT(2 2)
Can we do this in a pipe, by providing a select_ method, or do we need a st_select?
You mean filter_ (I make the same mistake quite often).
Here with a liberal test for either multi or single polygon:
i %>% filter(grepl("POLYGON", st_geometry_type(geometry)))
Simple feature collection with 1 feature and 2 fields
geometry type: GEOMETRY
dimension: XY
bbox: xmin: 1 ymin: 1 xmax: 2 ymax: 2
epsg (SRID): NA
proj4string: NA
a b geometry
1 1 2 POLYGON((2 2, 2 1, 1 1, 1 2...
Thanks, I meant filter indeed.
I see. Is that, like, OK enough? Similar (and better to my taste, although that doesn't mean anything):
i %>% filter(
st_geometry_type(.)
%in% c("POLYGON", "POINT") )
I don't have a strong opinion really, I know there's a lot of ways and mine is pretty ugly. The beauty is the evaluation going on in the general filter, possibly after mutate() etc.
Oh, but I didn't know about that use of the dot, nice!
This is a really great point. It's a neat example of interaction between sf and dplyr. I think your solution is good; I'd prefer to access a higher level interface, like st_is_geometrytype() or st_is_type() (don't know about the name).
i %>% filter(st_is_type(geometry, c("POLYGON", "POINT"))
#' or
i %>% filter(st_is_type(. , c("POLYGON", "POINT"))
I suggest st_is:
i %>% filter(st_is(. , "POINT")
i %>% filter(st_is(. , c("POLYGON", "POINT"))
@hadley you want to chime in?
Looks good. How to make it easy to specify e.g. both multi* and single in an easy way ?
Like st_is(sfc, "*polygon") (and allow lowercase)?
both the * and the lower case don't shine in obviousness, IMO. There's also
i %>% filter(st_dimension(.) == 2)
I think you're right that you want a vector function that takes either an sf or an sfc (I personally would prefer to write filter(i, st_is(geometry, "POINT")) and returns a logical vector. That will work with dplyr and base R, and is composable in other nice ways.
I don't have strong feelings about the function name. I do think you should keep the implementation as simple as possible, i.e. something like this:
st_is <- function(x, type) UseMethod("st_is")
st_is.sf <- function(x, type) {
st_is(x[[sf::st_geometry(x)]], type)
}
st_is.sfc <- function(x, type) {
vapply(x, inherits, type, FUN.VALUE = logical(1))
}
That possibly suggests the name should be st_inherits()
OK, I went with st_is:
st_is = function(x, type) UseMethod("st_is")
st_is.sf = function(x, type)
st_is(st_geometry(x), type)
st_is.sfc = function(x, type)
vapply(x, sf:::st_is.sfg, type, FUN.VALUE = logical(1))
st_is.sfg = function(x, type)
class(x)[2L] %in% type
I'm using is because
st_is(x, c("POINT", "MULTIPOINT"))sf objects, we check on the geometry of the complete record, where the whole record doesn't have a class of its ownI think it's a mistake to not base it on inherits(), which already has the behaviour you want, and doesn't use an arbitrary constant:
inherits(Sys.Date(), c("POSIXct", "Date"))
#> [1] TRUE
inherits(Sys.time(), c("POSIXct", "Date"))
#> [1] TRUE
Thanks - you're right, I got confused.
Most helpful comment
OK, I went with
st_is:I'm using
isbecausest_is(x, c("POINT", "MULTIPOINT"))sfobjects, we check on the geometry of the complete record, where the whole record doesn't have a class of its own