Detect if raster is within, without, or intersecting a SpatialPolygons object - r

I have many rasters for which I'd like to check if they are contain fully within a spatial polygon, fully without the spatial polygon, or intersecting with the spatial polygon (this could mean the polygon is fully within the raster, or the polygon and raster overlap). I'm doing this check to save me from time intensive masking when possible.
Here is an example:
# create 3 example rasters
r <- raster()
r[] <- rnorm(n = ncell(r))
e1 <- extent(c(45,55,45,50))
r1 <- crop(r,e1)
e2 <- extent(c(20,25,25,30))
r2 <- crop(r,e2)
e3 <- extent(c(38,55,57,65))
r3 <- crop(r,e3)
#create SpatialPolygons
x <- c(40,60)
y <- c(40,60)
m <- expand.grid(x,y)
m <- m[c(1,2,4,3),]
p1 <- Polygon(m)
p1 <- Polygons(list(p1),1)
x <- c(10,15)
y <- c(10,15)
m <- expand.grid(x,y)
m <- m[c(1,2,4,3),]
p2 <- Polygon(m)
p2 <- Polygons(list(p2),2)
x <- c(30,45)
y <- c(70,80)
m <- expand.grid(x,y)
m <- m[c(1,2,4,3),]
p3 <- Polygon(m)
p3 <- Polygons(list(p3),3)
poly <- SpatialPolygons(list(p1,p2,p3))
plotting these:
I will read in each raster separately and check if it is within, without, or intersecting the SpatialPolygons.
What do you think will be the most efficient way to do this in R? I have thousands of 4mb rasters that I'm planning to mask in parallel and would like this check to speed things up a bit.
Note, there's also this question: https://gis.stackexchange.com/questions/34535/detect-whether-there-is-a-spatial-polygon-in-a-spatial-extent
However, I don't think it gives the detail I'm looking for. For example, all of the rasters are within the extent of the spatial polygons, but not all are within the spatial polygons.
Functions like those in rgeos (gIntersects, gContains), would probably be handy. I'm not sure if these are most efficient, or how I should convert raster (or it's extent), to an sp object.
thanks!

You can also use gRelate for this. It returns a DE-9IM code that describes the relationships between interior, boundary and exterior components of the two geometries.
library(rgeos)
x <- sapply(rlist, function(x)
gsub('[^F]', 'T', gRelate(as(extent(x), 'SpatialPolygons'), poly)))
You could then compare the strings to relationships of interest. For example we might define within, disjoint, and overlaps as follows (but note that other some intersections are optional for given relationships - "within" is defined by GEOS as T*F**F***, "disjoint" as FF*FF****, and "overlaps" as T*T***T**):
pat <- c(TFFTFFTTT='within', FFTFFTTTT='disjoint', TTTTTTTTT='overlaps')
pat[x]
## TFFTFFTTT FFTFFTTTT TTTTTTTTT
## "within" "disjoint" "overlaps"
It seems marginally faster than the gContainsProperly/gIntersects approach, but #Tedward's post is more comprehensible, and more consistent with GEOS definitions (though the power to create specific relationship definitions might be desirable).
The elements of the DE-9IM strings represent, in order:
Does the interior of geometry A intersect the interior of geometry B?
Does the boundary of A intersect the interior of B?
Does the exterior of A intersect the interior of B?
Does the interior of geometry A intersect the boundary of geometry B?
Does the boundary of A intersect the boundary of B?
Does the exterior of A intersect the boundary of B?
Does the interior of geometry A intersect the exterior of geometry B?
Does the boundary of A intersect the exterior of B?
Does the exterior of A intersect the exterior of B?

This is what I did to solve the problem:
library(rgeos)
rlist <- list(r1,r2,r3)
lapply(rlist, function(raster) {
ei <- as(extent(raster), "SpatialPolygons")
if (gContainsProperly(poly, ei)) {
print ("fully within")
} else if (gIntersects(poly, ei)) {
print ("intersects")
} else {
print ("fully without")
}
})
Please let me know if you know of a more efficient solution.

Related

How do I find the overlap between two shapefiles?

I have two shapefiles (sf), one with polygons and one with points. As output I want a df showing which points fall within which polygons, something like this:
polygon overlap geometry
polygon1 point34 c(3478,234872)
polygon1 point56 c(23423,234982)
polygon2 point23 c(23498,2334)
polygon3 point45 c(872348,23847)
polygon3 point87 c(234982,1237)
polygon3 point88 c(234873,2873)
I assume I'll have to do something with st_intersection() but up to now I did not manage to get the desired output.
After fiddling around I came up with this solution, but I'm pretty sure it is not the most elegant. x and y are shapefiles, x with points and y with polygons.
count_overlap <- function(x, y){
f1 <- function(z){
r <- st_intersects(x,y[z,])
return(r)
}
l1 <- c(1:nrow(y))
l2 <- lapply(l1, f1)
l3 <- lapply(l2, unlist)
r <- sapply(l3, sum)
y$overlap <- r
return(y)
}
The result is the original y sf/dataframe with an added column called 'overlap' that shows the counts of points from x that fall within the polygon. Not exactly what I asked for in the question but a good outcome for me personally.
Try using over in sp:
library(sp)
out = over(pnt,plgn)
from ?over:
x = "SpatialPoints", y = "SpatialPolygons"
returns a numeric vector of length equal to the number of points; the number is the index (number) of the polygon of y in which a point falls; NA denotes the point does not fall in a polygon; if a point falls in multiple polygons, the last polygon is recorded.

Preserving IDs when using gCentroid to find centroids of multiple polygons in R

This seems like it should be simple. I have multiple polygons contained in one shapefile, and I am using the gCentroid() function from rgeos to create a bunch of centroid points. The function has an id argument which should return the parent geometry Ids if left unspecified, but this is either not working or I'm looking in the wrong spot or I misunderstood the argument.
Simple example:
library(terra)
library(rgeos)
library(sp)
v <- vect(system.file("ex/lux.shp", package="terra"))
v <- as(v, "Spatial")
#Clearly there are IDs here (albeit not unique)
v#data[["ID_1"]]
so when I go on to create centroids
cents <- gCentroid(v, byid = TRUE)
I don't see any associated "ID_1" slot. The issue is I will eventually be using these centroids to derive values from a raster, and will need the IDs to distinguish which polygon the values originate from.
The easy way would be
library(terra)
v <- vect(system.file("ex/lux.shp", package="terra"))
x <- centroids(v)
of with sf
library(sf)
y <- st_centroid(st_as_sf(v))
With rgeos you would need to do something along these lines
library(rgeos)
s <- as(v, "Spatial")
cents <- gCentroid(s, byid = TRUE)
atts <- data.frame(v)[as.integer(row.names(cents)), ]
s < SpatialPointsDataFrame(cents, atts)

Identifying which points in a regular lattice are within a polygon's boundaries

I would like to work out which points that define a regular lattice are within a polygon. The code below does this but VERY VERY slowly:
#the polygon that I want to check each point against
glasgow_single <- readShapePoly(
fn="data/clipped/glasgow_single"
)
#interpolated contains the coordinates of the regular grid
points_to_check <- expand.grid(
x=interpolated$x,
y=interpolated$y
)
#function to be called by plyr
fn <- function(X){
this_coord <- data.frame(lon=X["x"], lat=X["y"])
this_point <- SpatialPoints(this_coord)
out <- gContains(glasgow_single, this_point)
out <- data.frame(x=X["x"], y=X["y"], val=out)
return(out)
}
#plyr call
vals <- adply(points_to_check, 1, fn, .progress="text")
vals$val <- as.numeric(vals$val)
Taking into account both thinking time and computing time, is there a much faster way of doing this?
Yes, there's a much better approach. For this and many other topological operations, the rgeos package has you well covered. Here, you're wanting rgeos::gWithin():
## Required packages
library(rgdal)
library(raster) ## For example polygon & functions used to make example points
library(rgeos)
## Reproducible example
poly <- readOGR(system.file("external", package="raster"), "lux")[1,]
points <- as(raster(extent(poly)), "SpatialPoints")
proj4string(points) <- proj4string(poly)
## Test which points fall within polygon
win <- gWithin(points, poly, byid=TRUE)
## Check that it works
plot(poly)
points(points, col=1+win)

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)

Export R plot to shapefile

I am fairly new to R, but not to ArcView. I am plotting some two-mode data, and want to convert the plot to a shapefile. Specifically, I would like to convert the vertices and the edges, if possible, so that I can get the same plot to display in ArcView, along with the attributes.
I've installed the package "shapefiles", and I see the convert.to.shapefile command, but the help doesn't talk about how to assign XY coords to the vertices.
Thank you,
Tim
Ok, I'm making a couple of assumptions here, but I read the question as you're looking to assign spatial coordinates to a bipartite graph and export both the vertices and edges as point shapefiles and polylines for use in ArcGIS.
This solution is a little kludgey, but will make shapefiles with coordinate limits xmin, ymin and xmax, ymax of -0.5 and +0.5. It will be up to you to decide on the graph layout algorithm (e.g. Kamada-Kawai), and project the shapefiles in the desired coordinate system once the shapefiles are in ArcGIS as per #gsk3's suggestion. Additional attributes for the vertices and edges can be added where the points.data and edge.data data frames are created.
library(igraph)
library(shapefiles)
# Create dummy incidence matrix
inc <- matrix(sample(0:1, 15, repl=TRUE), 3, 5)
colnames(inc) <- c(1:5) # Person ID
rownames(inc) <- letters[1:3] # Event
# Create bipartite graph
g.bipartite <- graph.incidence(inc, mode="in", add.names=TRUE)
# Plot figure to get xy coordinates for vertices
tk <- tkplot(g.bipartite, canvas.width=500, canvas.height=500)
tkcoords <- tkplot.getcoords(1, norm=TRUE) # Get coordinates of nodes centered on 0 with +/-0.5 for max and min values
# Create point shapefile for nodes
n.points <- nrow(tkcoords)
points.attr <- data.frame(Id=1:n.points, X=tkcoords[,1], Y=tkcoords[,2])
points.data <- data.frame(Id=points.attr$Id, Name=paste("Vertex", 1:n.points, sep=""))
points.shp <- convert.to.shapefile(points.attr, points.data, "Id", 1)
write.shapefile(points.shp, "~/Desktop/points", arcgis=TRUE)
# Create polylines for edges in this example from incidence matrix
n.edges <- sum(inc) # number of edges based on incidence matrix
Id <- rep(1:n.edges,each=2) # Generate Id number for edges.
From.nodes <- g.bipartite[[4]]+1 # Get position of "From" vertices in incidence matrix
To.nodes <- g.bipartite[[3]]-max(From.nodes)+1 # Get position of "To" vertices in incidence matrix
# Generate index where position alternates between "From.node" to "To.node"
node.index <- matrix(t(matrix(c(From.nodes, To.nodes), ncol=2)))
edge.attr <- data.frame(Id, X=tkcoords[node.index, 1], Y=tkcoords[node.index, 2])
edge.data <- data.frame(Id=1:n.edges, Name=paste("Edge", 1:n.edges, sep=""))
edge.shp <- convert.to.shapefile(edge.attr, edge.data, "Id", 3)
write.shapefile(edge.shp, "~/Desktop/edges", arcgis=TRUE)
Hope this helps.
I'm going to take a stab at this based on a wild guess as to what your data looks like.
Basically you'll want to coerce the data into a data.frame with two columns containing the x and y coordinates (or lat/long, or whatever).
library(sp)
data(meuse.grid)
class(meuse.grid)
coordinates(meuse.grid) <- ~x+y
class(meuse.grid)
Once you have it as a SpatialPointsDataFrame, sp provides some decent functionality, including exporting shapefiles:
writePointsShape(meuse.grid,"/home/myfiles/wherever/myshape.shp")
Relevant help files examples are drawn from:
coordinates
SpatialPointsDataFrame
readShapePoints
At least a few years ago when I last used sp, it was great about projection and very bad about writing projection information to the shapefile. So it's best to leave the coordinates untransformed and manually tell Arc what projection it is. Or use writeOGR rather than writePointsShape.

Resources