I am trying to get the intersection of two shapefiles (census tracts that fall within the boundaries of certain metropolitan areas). I am able to successfully get the intersecting features, however when I try to convert the output of sf_intersect to a SpatialPolygonsDataframe I get the error:
"Error in as_Spatial(from) : conversion from feature type
sfc_GEOMETRY to sp is not supported"
This is my code:
library(sf)
library(dplyr)
library(tigris)
library(sp)
#download shapefiles corresponding to metro areas
metro_shapefiles<-core_based_statistical_areas(cb = FALSE, year = 2016)
#convert to sf and filter
metro_shapefiles<-st_as_sf(metro_shapefiles)%>%filter(GEOID==31080 )
#Data for California
census_tracts_california<-tracts(state="CA",year=2016)
census_tracts_california<-st_as_sf(census_tracts_california)
#INTERSECT AND CONVERT BACK TO SP
census_tracts_intersected1<-st_intersection(census_tracts_california,
metro_shapefiles)
#back to spatial
census_tracts_intersected1<-as(census_tracts_intersected1,"Spatial")
The error message is telling you you can't convert an sfc_GEOMETRY to a Spatial object. There is no sp equivalent object.
In your intersection result you have a mixture of geometries (and hence, you're returned an sfc_GEOMETRY as your 'geometry'). You can see all the geometries here:
types <- vapply(sf::st_geometry(census_tracts_intersected1), function(x) {
class(x)[2]
}, "")
unique(types)
# [1] "POLYGON" "MULTILINESTRING" "MULTIPOLYGON"
If you want, you can extract each type of geometry, and convert those to SP individually:
lines <- census_tracts_intersected1[ grepl("*LINE", types), ]
polys <- census_tracts_intersected1[ grepl("*POLYGON", types), ]
spLines <- as(lines, "Spatial")
spPolys <- as(polys, "Spatial")
Additional Information
I mentioned in the comments you could use st_join. However, this may not give you the result you want. Within sf library there are the geometric binary predicates, such as ?st_intersects, and geometric operations such as ?st_intersection
The predicates return a sparse (default) or dense matrix telling you with which geometry of y each geometry of x intersects. If you use this within st_join, it will return the (original) geometries that intersect, rather than the sparse matrix.
Whereas the operations (e.g. st_intersection) will compute the intersection, and return new geometries.
Example use
The predicates (st_intersects) can be used inside st_join, and they will return the original geometries which 'intersect'
sf_join <- sf::st_join(census_tracts_california, metro_shapefiles, join = st_intersects)
In this case this gives a single type of object
types <- vapply(sf::st_geometry(sf_join), function(x) {
class(x)[2]
}, "")
unique(types)
# [1] "MULTIPOLYGON"
## so you can convert to a Spatial object
spPoly <- as(sf_join, "Spatial")
But you need to decide if the result of st_intersect is what you're after, or whether you need the new geometries given by st_intersection.
Further reading
information on each join is on the sf blog.
spatial predicates and examples of what the different operations do are on wikipedia (with some good illustrations)
Credit to user #lbussett for their description on the difference between st_intersect and st_intersection
Conversion to a Spatial object can't handle mixed lines and polygons. After the intersection you can extract just the polygons (and discard any lines) using:
st_collection_extract("POLYGON")
Your example doesn't seem to fail anymore, so I've created a new example that intersects two polygons, with a shared side. This results in an intersection output of a polygon and a line.
On the second attempt I've piped the intersection through the st_collection_extract() function prior to successful conversion to a Spatial object.
library(sf)
library(dplyr)
library(sp)
#Create some boxes
BoxA <- st_polygon(list(cbind(c(0,0,2,2,0),c(0,2,2,0,0))))
BoxB <- st_polygon(list(cbind(c(1,1,3,3,1),c(1,3,3,1,1))))
BoxC <- st_polygon(list(cbind(c(2,2,4,4,2),c(0,2,2,0,0))))
#Create a funny shaped union to help demonstrate the intersection issue
BoxAB <- st_union(BoxA,BoxB)
plot(BoxAB)
plot(BoxC,add=TRUE,border="blue")
Example polygons to intersect
#Intersect of BoxAB with BoxC results in a line and a polygon
BoxIntersects<-st_intersection(BoxAB,BoxC)
plot(BoxIntersects)
Intersection made up of a polygon and a line
#back to spatial fails
SpatialVersionOfIntersects<-as(BoxIntersects,"Spatial")
Error in .as_Spatial(from, cast, IDs) :
conversion from feature type sfc_GEOMETRY to sp is not supported
#Intersect again, but this time extract only the polygons
BoxIntersects<-st_intersection(BoxAB,BoxC) %>% st_collection_extract("POLYGON")
#back to spatial suceeds!
SpatialVersionOfIntersects<-as(BoxIntersects,"Spatial")
Related
I need to extend the boundary of a field (only boundary) by x meters. I tried using gBuffer from rgeos R package - output of the transformation gives me only boundary of the field and rest polygons inside the field are lost with data.
How I can use gBuffer / any other way to extend only boundary of spatial polygon object (shape file) by 10m and keeping everything intact (inside polygons and data)
Tried Code -
field <- raster::shapefile("test.shp")
class(field)
plot(field)
View(field#data)
field <- sp::spTransform(field,CRS("+init=epsg:32632"))
plot(field)
field10m <- rgeos::gBuffer(field , width = 10)
plot(field10m)
Test shapefile can be downloaded from here https://drive.google.com/file/d/1s4NAinDeBow95hxr6gELHHkhwiR3z6Z9/view?usp=sharing
I suggest you consider a workflow based on the {sf} package; it makes for a bit clearer code than sp and rgeos (it will use the same geometry engine, but hide the gritty parts under the hood).
The code preserves all data features (actually, only one - a column named Rx) of your shapefile.
Note that since the yellow element / Rx = 120 / consists of multiple polygons each of them is buffered, resulting in overlaid features. This is expected outcome.
Should this be undesired behavior you can consider using a dplyr::group_by(Rx) followed by dplyr::summarise() to dissolve the internal boundary lines before applying the sf::st_buffer() call.
library(sf)
library(dplyr)
library(mapview) # needed only for the final overview
library(leafsync) # dtto.
test_map <- sf::st_read("./Map/test.shp")
# find an appropriate projected metric CRS
crsuggest::suggest_crs(test_map, type = "projected")
result <- test_map %>%
sf::st_transform(5683) %>% # transform to a metric CRS
sf::st_buffer(10) # buffer by 10 meters
# a visual check / note how the polygons are overlaid
leafsync::latticeview(mapview::mapview(test_map),
mapview::mapview(result))
1. The problem
I'm trying to extract the intersection of two polygons shapes in R. The first is the watershed polygon "ws_polygon_2", and the second is the Voronoi polygons of 5 rain gauges which was constructed from the Excel sheet "DATA.xlsx", both available here: link.
The code is the following:
#[1] Montagem da tabela de coordenadas dos postos pluviométricos
library(sp)
library(readxl)
dados_precipitacao_1985 <- read_excel(path="C:/Users/.../DATA.xlsx")
coordinates(dados_precipitacao_1985) <- ~ x + y
proj4string(dados_precipitacao_1985) <- CRS("+proj=longlat +datum=WGS84")
d_prec <- spTransform(dados_precipitacao_1985, CRSobj = "+init=epsg:3857")
#[2] Coleta dos dados espaciais da bacia hidrográfica
library(rgdal)
bacia_Caio_Prado <- readOGR(dsn="C:/Users/...", layer="ws_polygon_2")
bacia_WGS <- spTransform(bacia_Caio_Prado, CRSobj = "+proj=longlat +datum=WGS84")
bacia_UTM <- spTransform(bacia_Caio_Prado, CRSobj = "+init=epsg:3857")
#[3] Poligonos de Thiessen - 1 INTERPOLAÇÃO
library(dismo)
library(rgeos)
library(raster)
library(mapview)
limits_voronoi_WGS <- c(-40.00,-38.90,-5.00,-4.50)
v_WGS <- voronoi(dados_precipitacao_1985, ext=limits_voronoi_WGS)
bc <- aggregate(bacia_WGS)
u_WGS_1 <- gIntersection(spgeom1 = v_WGS, spgeom2 = bc,byid=TRUE)
u_WGS_2 <- intersect(bc, v_WGS)
When I apply the intersect function, the variable returned u_WGS_2 is a spatial polygon data frame with only 4 features, instead of 5. The Voronoi object v_WGS has 5 features as well.
By other hand, when I apply the gIntesection function, I get 5 features. However, the u_WGS_1 object is a spatial polygon only and I loss the rainfall data.
I'd like to know if I am committing any mistake or if there is any way to get the 5 features aggregated with the rainfall data in a spatial polygon data frame through the intersect function.
My objective is to transform this spatial polygon data frame with the rainfall data for each Voronoi polygon in a raster through the rasterize function later to compare with other interpolating results and satellite data.
Look these results. The first one is when I get the SPDF (Spatial Polygon Data Frame) I want, but missing the 5º feature. The second is the one I get with all the features I want, but missing the rainfall data.
spplot(u_WGS_2, 'JAN')
plot(u_WGS_1)
2. What I've tried
I look into the ws_polygon_2 shape searching for any other unwanted polygon who would pollute the shape and guide to this results. The shape is composed by only one polygon feature, the correct watershed feature.
I tried to use the aggregate function, as above, and as I saw in this tutorial. But I got the same result.
I tried to create a SPDF with de u_WGS_1 and the d_precSpatial Point Data Frame object. Actually, I'm working on it. And if it is the correct answer to my trouble, please help me with some code.
Thank you!
This is not an issue when using st_intersection() from sf, which retains the data from both data sets. Mind that dismo::voronoi() is compatible with sp objects only, so the precipitation data needs to be available in that format, at least temporarily. If you do not feel comfortable with sf and prefer to continue working with Spatial* objects after the actual intersection, simply invoke the as() method upon the output sf object as shown below.
library(sf)
#[1] Montagem da tabela de coordenadas dos postos pluviométricos
dados_precipitacao_1985 <- readxl::read_excel(path="data/DATA.xlsx")
dados_precipitacao_1985 <- st_as_sf(dados_precipitacao_1985, coords = c("x", "y"), crs = 4326)
dados_precipitacao_1985_sp <- as(dados_precipitacao_1985, "Spatial")
#[2] Coleta dos dados espaciais da bacia hidrográfica
bacia_Caio_Prado <- st_read(dsn="data/SHAPE_CORRIGIDO", layer="ws_polygon_2")
#[3] Poligonos de Thiessen - 1 INTERPOLAÇÃO
limits_voronoi_WGS <- c(-40.00,-38.90,-5.00,-4.50)
v_WGS <- dismo::voronoi(dados_precipitacao_1985_sp, ext=limits_voronoi_WGS)
v_WGS_sf <- st_as_sf(v_WGS)
u_WGS_3 <- st_intersection(bacia_Caio_Prado, v_WGS_sf)
plot(u_WGS_3[, 6], key.pos = 1)
The missing polygon is removed because it is invalid
library(raster)
bacia <- shapefile("SHAPE_CORRIGIDO/ws_polygon_2.shp")
rgeos::gIsValid(bacia)
#[1] FALSE
#Warning message:
#In RGEOSUnaryPredFunc(spgeom, byid, "rgeos_isvalid") :
# Ring Self-intersection at or near point -39.070555560000003 -4.8419444399999998
The self-intersection is here:
zoom(bacia, ext=extent(-39.07828, -39.06074, -4.85128, -4.83396))
points(cbind( -39.070555560000003, -4.8419444399999998))
Invalid polygons are removed as they are assumed to have been produced by intersect. In this case, the invalid data was already there and should have been retained. I will see if I can fix that.
I have a shapefile named "ind_adm" and a SpatialPointsDataFrame called "pnts". The "pnts" contains points generated at random, and some of the points overlap with the polygon. See picture below.
Now, I want do do a point in polygon analysis, i.e. I want to find out which points lie inside the gray polygon representing the boundary of India. For this I am using the over() function in the sp library.
pt.in.poly <- sp::over(ind_adm, pnts, fn = mean) #do the join
However, the output I am getting is
>pt.in.poly
values
0 6.019467
I should actually get the index of the points that are "in" the polygon.
Where am I going wrong?
Found this concise and intuitive syntax for over:
pnts[ind_adm,]
from this Intro document
You should not supply a function. You are aggregating the attribute values of your points over the geometry of the polygon, (i.e. the number returned is the mean of the attribute of the points that fall within the polygon). In addition you have your x and y the wrong way round for what you want to do. Should be...
over( pnts , ind_adm , fn = NULL)
You can use point.in.poly fom spatialEco package. It "intersects point and polygon feature classes and adds polygon attributes to points".
library(spatialEco)
new_shape <- point.in.poly(pnts, ind_adm)
You could also use the st_intersection function from the sf package:
Load the library
library(sf)
Create a simple feature geometry (polygon) from your polygon
ind_adm <- st_as_sf(ind_adm)
Create a simple feature geometry (point) from your points of interest
(24047 is the EPSG code for India)
pnts <- st_as_sf(pnts) %>% st_set_crs(., 24047)
Keep only the points inside the polygon
kept_points <- st_intersection(ind_adm, pnts)
I'm figuring out how to do a Intersection (Spatial Join) between point and polygons from shapefiles. My idea is to get the closest points and those points that match completely inside the polygons. In ARGIS there's a function for match option named CLOSEST and they have defined by: "The feature in the join features that is closest to a target feature is matched. It is possible that two or more join features are the same distance away from the target feature. When this situation occurs, one of the join features is randomly selected as the matching feature."
I have a function to intersect points into polygons, it was kindly contributed by Lyndon Estes at the r-sig-geo list and the code works very well when all the polygons have filled all the area. The second case is known as a Spatial join distance and in ArcGIS is know as INTERSECT when match_option is CLOSEST, as ArcGIS does. So, you can modify the minimal distance between the point and the polygon when the area is not filled by all polygons.
Here's the data and the function of the first INTERSECT:
library(rgeos)
library(sp)
library(maptools)
library(rgdal)
library(sp)
xy.map <- readShapeSpatial("http://www.udec.cl/~jbustosm/points.shp")
manzana.map <- readShapeSpatial("http://www.udec.cl/~jbustosm/manzanas_from.shp" )
IntersectPtWithPoly <- function(x, y) {
# Extracts values from a SpatialPolygonDataFrame with SpatialPointsDataFrame, and appends table (similar to
# ArcGIS intersect)
# Args:
# x: SpatialPoints*Frame
# y: SpatialPolygonsDataFrame
# Returns:
# SpatialPointsDataFrame with appended table of polygon attributes
# Set up overlay with new column of join IDs in x
z <- overlay(y, x)
# Bind captured data to points dataframe
x2 <- cbind(x, z)
# Make it back into a SpatialPointsDataFrame
# Account for different coordinate variable names
if(("coords.x1" %in% colnames(x2)) & ("coords.x2" %in% colnames(x2))) {
coordinates(x2) <- ~coords.x1 + coords.x2
} else if(("x" %in% colnames(x2)) & ("x" %in% colnames(x2))) {
coordinates(x2) <- ~x + y
}
# Reassign its projection if it has one
if(is.na(CRSargs(x#proj4string)) == "FALSE") {
x2#proj4string <- x#proj4string
}
return(x2)
}
test<-IntersectPtWithPoly (xy.map,manzana.map)
Sharing some ideas with Lyndon, he told me this:
I think the easiest thing to do would be to put a buffer around each of the points (you could specify 50 m if it is in projected coordinates), converting them to polygons, and then your task becomes an intersection of two different polygon objects.
I haven't done this type of operation in R, but I suspect you could find your answer with the following functions:
library(sp)
?over
library(rgeos)
?gBuffer
?gIntersects
I suggest putting up a subset of your data illustrating the problem, and then maybe someone else who has a better idea on polygon to polygon intersects/overlays could suggest the method.
should be made in the points radius which are in the shapefile in order to make them get into the nearest polygon.
I know that this functions could help to achive it.
library(sp)
?over
library(rgeos)
?gBuffer
?gIntersects
I'm working on it, so any comment or help, would be very apreciated!
I have got that it's possible doing polygon to polygon overlays using sp andrgeos. You'd need to load rgeos after you load sp.
library(rgeos)
over(polygon1, polygon2)
I am working with shapefiles in R, one is point.shp the other is a polygon.shp.
Now, I would like to intersect the points with the polygon, meaning that all the values from the polygon should be attached to the table of the point.shp.
I tried overlay() and spRbind in package sp, but nothing did what I expected them to do.
Could anyone give me a hint?
With the new sf package this is now fast and easy:
library(sf)
out <- st_intersection(points, poly)
Additional options
If you do not want all fields from the polygon added to the point feature, just call dplyr::select() on the polygon feature before:
library(magrittr)
library(dplyr)
library(sf)
poly %>%
select(column-name1, column-name2, etc.) -> poly
out <- st_intersection(points, poly)
If you encounter issues, make sure that your polygon is valid:
st_is_valid(poly)
If you see some FALSE outputs here, try to make it valid:
poly <- st_make_valid(poly)
Note that these 'valid' functions depend on a sf installation compiled with liblwgeom.
If you do overlay(pts, polys) where pts is a SpatialPointsDataFrame object and polys is a SpatialPolygonsDataFrame object then you get back a vector the same length as the points giving the row of the polygons data frame. So all you then need to do to combine the polygon data onto the points data frame is:
o = overlay(pts, polys)
pts#data = cbind(pts#data, polys[o,])
HOWEVER! If any of your points fall outside all your polygons, then overlay returns an NA, which will cause polys[o,] to fail, so either make sure all your points are inside polygons or you'll have to think of another way to assign values for points outside the polygon...
You do this in one line with point.in.poly fom spatialEco package.
library(spatialEco)
new_shape <- point.in.poly(pts, polys)
from the documentation: point.in.poly "intersects point and polygon feature classes and adds polygon attributes to points".