sp::over() for point in polygon analysis - r

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)

Related

Calculate minimum distance between multiple polygons with R

I'm still somewhat new to R and the sf package...
I have two sets of multipolygon data that I am trying to analyze. My first set of polygons (fires) contains hundreds of wildfire perimeters. The second set (towns) contains hundreds of urban areas boundaries.
For each fire, I would like to calculate the distance to the closest town (fire polygon edge to closest town polygon edge), and add that as a field to each fire.
So far I have mostly been using the sf package for spatial data. In my searches, I can only find minimum distance methods for polygons to points, points to points, lines to points, etc. but cannot seem to find polygon to polygon examples. Any help to send me in the right direction would be much appreciated! Thank you.
#TimSalabim Thank you for sending me in the right direction. I was able to accomplish what I was after. Maybe not the most elegant solution, but it worked.
# create an index of the nearest feature
index <- st_nearest_feature(x = poly1, y = poly2)
# slice based on the index
poly2 <- poly2 %>% slice(index)
# calculate distance between polygons
poly_dist <- st_distance(x = poly1, y= poly2, by_element = TRUE)
# add the distance calculations to the fire polygons
poly1$distance <- poly_dist

st_intersection to spatialpolygon dataframe not working

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")

How to pick up the information for the nearest associated polygon to points using R?

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)

How to calculate geo-distance from a polygon?

I have a shapefile with 50+ different polygonal shapes (representing 50+ different regions) and 10,000+ data points that are supposed to be present in one of the regions. The thing is, the 10,000+ points are already coded with a region they are supposed to be in, and I want to figure out how far they are from this coded region in geo-spatial distance.
My current approach (code below), which involves converting shapefiles to owin objects from the sp library and using distfun gets me distances in lat,long euclidean space. But I would like to get geo-spatial distances (eventually to convert to km). Where should I go next?
#basically cribbed from http://cran.r-project.org/web/packages/spatstat/vignettes/shapefiles.pdf (page 9)
shp <- readShapeSpatial("myShapeFile.shp", proj4string=CRS("+proj=longlat +datum=WGS84"))
regions <- lapply(slot(shp, "polygons"), function(x) SpatialPolygons(list(x)))
windows <- lapply(regions, as.owin)
# need to convert this to geo distance
distance_from_region <- function(regionData, regionName) {
w <- windows[[regionName]]
regionData$dists <- distfun(w)(regionData$lat, regionData$long)
regionData
}
I'd project the data to a euclidean (or near euclidean) coordinate system - unless you are spanning a large chunk of the globe then this is feasible. Use spTransform from maptools or sp or rgdal (I forget which) and convert to a UTM zone near your data.
You also might do better with package rgeos and the gDistance function:
gDistance by default returns the cartesian minimum distance
between the two geometries in the units of the current projection.
If your data is over a large chunk of globe then... tricky... 42...
Barry

Intersecting Points and Polygons in R

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".

Resources