In R I performed an analysis on a set of X, Y coordinates producing a Voronoi diagram, and I then created a border. Here I intersect the diagram and the border, and attempt to get the area of the resulting polygons. The areas for the interior "hole" polys are correct, but the edge polygons appear to maintain their original exaggerated size. A link to my data is here:
https://drive.google.com/a/ruths.ai/file/d/0B8QG4cbDqH0UaGM2VkkxZHZkZTA/edit?usp=sharing
Code illustrating the problem is here:
# Read in shapefiles.
# Files are located at:
# https://drive.google.com/a/ruths.ai/file/d/0B8QG4cbDqH0UaGM2VkkxZHZkZTA/edit?usp=sharing
require(sp)
require(rgeos)
SPDF <- readShapeSpatial("SPDF.shp")
SpP <- readShapeSpatial("SpP.shp")
# Examine plots
plot(SPDF)
SPDF#polygons[[337]]#area # Too large; want it cut off
SPDF#polygons[[339]]#area # Hole poly; area correct
gArea(SPDF[339,]) # Provides same area
gArea(SPDF[337,]) # Still provide wrong answer for problem
# poly # 337
# Merge polys using gDifference
D <- gDifference(SpP, SPDF, byid = TRUE)
plot(D)
# Seems to work, but areas now have a couple of problems.
# I pick apart D using the plotOrder slot to separate
# polys that are holes versus those that are not, allowing
# me to get the correct area for "hole" polys, but the
# edge polygons are still not correct, maintaining their
# area estimates from the original SPDF data frame.
areas <- vector()
for (i in 337:339){ # 337 = exterior poly, 338 and 339 are holes
po <- D#polygons[[i]]#plotOrder
if (max(po) == 2) {
areas[i] <- D#polygons[[i]]#Polygons[[2]]#area
} else {
areas[i] <- D#polygons[[i]]#area
}
}
areas
# How does one get the right areas for the edges that should be cut
# off by the intersection?
I should have used gIntersection instead of gDifference. For some reason that I still do not understand, the plot after using gDifference seemed correct, which was the main reason for the confusion.
Related
What I´m looking for is a way to rasterize (or fasterize) geometries within each tile of a certain extent step by step and join the parts of the rasterized geometries to entire raster objects.
## create sample data
# create a frame
library(sf)
ob = st_sf(st_sfc(st_polygon(list(rbind(c(0,0), c(0,9), c(6,9), c(6,0), c(0,0)))))
# create tiles
library(GSIF)
tl <- getSpatialTiles(as(ob, 'Spatial'), block.x=3, overlap.percent=2)
plot(tl)
# create sample polygons
g <- st_sfc(st_point(c(1,2)), st_point(c(5,6)), st_point(c(2,4)), st_point(c(3,3)), st_point(c(3,4)), st_point(c(4,5)))
g.b <- st_buffer(g,0.6)
p <- st_sf(value = ceiling(10*runif(6)),
geometry = st_sfc(g.b))
plot(p, add=TRUE)
Cropping the polygons (or parts of polygons) that are within each tile works fine , i guess. My actual goal is to process the data tile by tile. It looks like the following loop does this and also joins the parts of the polygons to entire polygons. Well, the output (cr) is the same as the input (p)... I thought that this step might be necessary in order to rasterize the resulting (parts of) polygons in the next step. i didn´t expect that the polygons would be joined. I was trying to build a loop based on the intermediate result, therefore following lines are add:
# crop polygons (or parts) for each tile
result <- p
for(i in 1:length(tl)) {cr <- rbind(result, st_crop(p, tl[i]))}
cr <- cr[-7,]
library(scales)
plot(st_geometry(cr), col=alpha("white", 0.5), add=TRUE)
However, I struggle rasterizing the (intermediate) results.
Try this:
myF <- function(i){return(st_crop(p, tl[i]))}
cr <- do.call(rbind, lapply(1:length(tl), myF))
In Software like ArcMap one can create centroids for polygons within a polygon. In cases like the one shown below this is necessary.
In R it is possible to calculate centroids of spatial polygons with rgeos::gCentroid(). However there is no way to force the calculation of centroids within the polygon.
library(rgdal)
library(rgeos)
x <- readWKT("POLYGON ((1441727.5096940901130438 6550163.0046194596216083,
1150685.2609429201111197 6669225.7427449300885201,
975398.4520359700545669 6603079.7771196700632572,
866257.6087542800232768 6401334.5819626096636057,
836491.9242229099618271 6106985.0349301798269153,
972091.1537546999752522 5835786.5758665995672345,
1547561.0546945100650191 5782869.8033663900569081,
1408654.5268814601004124 5600968.3978968998417258,
720736.4843787000281736 5663807.0652409195899963,
598366.4479719599476084 6001151.4899297598749399,
654590.5187534400029108 6341803.2128998702391982,
869564.9070355399744585 6784981.1825891500338912,
1451649.4045378800947219 6788288.4808704098686576,
1441727.5096940901130438 6550163.0046194596216083))")
plot(x)
This is the polygon x
gCentroid() creates a centroid which in this specific case is located outside of the polygon. Despite being geometrically correct, some applications require centroids within the polygon, as they can be calculated by ArcMap.
xCent <- gCentroid(x, byid = TRUE)
points(xCent, col = "red", pch = 16)
A desired output (from ArcMap) looks like this:
Is there any possibility to generate centroids like this in R?
EDIT:
After some digging, it turns out that ArcMap picks a random point within the Polygon:
"For an input polygon: the output point will be inside the polygon."
Thus the question has to be: is there a function that creates a point at any random position WITHIN the polygons?
sf solution
With the advent of the sf package, things got a bit easier. Just use:
library(sf)
y <- st_as_sf(x) # only necessary when you don't already have an sf object
st_point_on_surface(y)
It "returns a point guaranteed to be on the (multi)surface."
sp solution
As pointed out in the updates of the Question, it seems that ArcMap is just putting a point at a random location within the polygon. This can be achieved by gPointsOnSurface(..., n = 1, type = 'random') as well.
xCent2 <- gPointOnSurface(x, byid = T)
points(xCent2, col = "blue", pch = 16)
I wrote this function which first finds the centroid and, if it is not on within (i.e. it does not overlap / intersect the polygon), it is substituted by a point on the surface. Furhtermore, it returns a new column which indicates if a point is the real centroid or not.
gCentroidWithin <- function(pol) {
require(rgeos)
pol$.tmpID <- 1:length(pol)
# initially create centroid points with gCentroid
initialCents <- gCentroid(pol, byid = T)
# add data of the polygons to the centroids
centsDF <- SpatialPointsDataFrame(initialCents, pol#data)
centsDF$isCentroid <- TRUE
# check whether the centroids are actually INSIDE their polygon
centsInOwnPoly <- sapply(1:length(pol), function(x) {
gIntersects(pol[x,], centsDF[x, ])
})
if(all(centsInOwnPoly) == TRUE){
return(centsDF)
}
else {
# substitue outside centroids with points INSIDE the polygon
newPoints <- SpatialPointsDataFrame(gPointOnSurface(pol[!centsInOwnPoly, ],
byid = T),
pol#data[!centsInOwnPoly,])
newPoints$isCentroid <- FALSE
centsDF <- rbind(centsDF[centsInOwnPoly,], newPoints)
# order the points like their polygon counterpart based on `.tmpID`
centsDF <- centsDF[order(centsDF$.tmpID),]
# remove `.tmpID` column
centsDF#data <- centsDF#data[, - which(names(centsDF#data) == ".tmpID")]
cat(paste(length(pol), "polygons;", sum(centsInOwnPoly), "actual centroids;",
sum(!centsInOwnPoly), "Points corrected \n"))
return(centsDF)
}
I have a SpatialPolygonsDataFrame with 11589 objects of class "polygons". 10699 of those objects consists of exactly 1 polygon, however the rest of those objects consists of multiple polygons (2 to 22).
If an object of consists of multiple polygons, three scenarios are possible:
Sometimes, those additional polygons describe a "hole" in the geographic ara describe by the first polygon in the object of class "polygons".
Sometimes, those additional polygons describe additional geographic areas, i.e. the shape of the region is quite complex and described by putting together multiple parts.
Sometimes, it might be a mix of both, 1) and 2).
Stackoverflow helped me to plot such an spatial object properly (Plot spatial area defined by multiple polygons).
However, I am still not able to answer how to determine whether a point (defined by longitude/latitude) is in a polygon.
Below is my code. I tried to apply the function point.in.polygon in the sp package, but found no way how it could handle such an object which consists of multiple polygons/holes.
# Load packages
# ---------------------------------------------------------------------------
library(maptools)
library(rgdal)
library(rgeos)
library(ggplot2)
library(sp)
# Get data
# ---------------------------------------------------------------------------
# Download shape information from the internet
URL <- "http://www.geodatenzentrum.de/auftrag1/archiv/vektor/vg250_ebenen/2012/vg250_2012-01-01.utm32s.shape.ebenen.zip"
td <- tempdir()
setwd(td)
temp <- tempfile(fileext = ".zip")
download.file(URL, temp)
unzip(temp)
# Get shape file
shp <- file.path(tempdir(),"vg250_0101.utm32s.shape.ebenen/vg250_ebenen/vg250_gem.shp")
# Read in shape file
map <- readShapeSpatial(shp, proj4string = CRS("+init=epsg:25832"))
# Transform the geocoding from UTM to Longitude/Latitude
map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))
# Pick an geographic area which consists of multiple polygons
# ---------------------------------------------------------------------------
# Output a frequency table of areas with N polygons
nPolys <- sapply(map#polygons, function(x)length(x#Polygons))
# Get geographic area with the most polygons
polygon.with.max.polygons <- which(nPolys==max(nPolys))
# Get shape for the geographic area with the most polygons
Poly.coords <- map[which(nPolys==max(nPolys)),]
# Plot
# ---------------------------------------------------------------------------
# Plot region without Google maps (ggplot2)
plot(Poly.coords, col="lightgreen")
# Find if a point is in a polygon
# ---------------------------------------------------------------------------
# Define points
points_of_interest <- data.frame(long=c(10.5,10.51,10.15,10.4),
lat =c(51.85,51.72,51.81,51.7),
id =c("A","B","C","D"), stringsAsFactors=F)
# Plot points
points(points_of_interest$long, points_of_interest$lat, pch=19)
You can do this simply with gContains(...) in the rgeos package.
gContains(sp1,sp2)
returns a logical depending on whether sp2 is contained within sp1. The only nuance is that sp2 has to be a SpatialPoints object, and it has to have the same projection as sp1. To do that, you would do something like this:
point <- data.frame(lon=10.2, lat=51.7)
sp2 <- SpatialPoints(point,proj4string=CRS(proj4string(sp1)))
gContains(sp1,sp2)
Here is a working example based on the answer to your previous question.
library(rgdal) # for readOGR(...)
library(rgeos) # for gContains(...)
library(ggplot2)
setwd("< directory with all your files >")
map <- readOGR(dsn=".", layer="vg250_gem", p4s="+init=epsg:25832")
map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))
nPolys <- sapply(map#polygons, function(x)length(x#Polygons))
region <- map[which(nPolys==max(nPolys)),]
region.df <- fortify(region)
points <- data.frame(long=c(10.5,10.51,10.15,10.4),
lat =c(51.85,51.72,51.81,51.7),
id =c("A","B","C","D"), stringsAsFactors=F)
ggplot(region.df, aes(x=long,y=lat,group=group))+
geom_polygon(fill="lightgreen")+
geom_path(colour="grey50")+
geom_point(data=points,aes(x=long,y=lat,group=NULL, color=id), size=4)+
coord_fixed()
Here, point A is in the main polygon, point B is in a lake (hole), point C is on an island, and point D is completely outside the region. So this code checks all of the points using gContains(...)
sapply(1:4,function(i)
list(id=points[i,]$id,
gContains(region,SpatialPoints(points[i,1:2],proj4string=CRS(proj4string(region))))))
# [,1] [,2] [,3] [,4]
# id "A" "B" "C" "D"
# TRUE FALSE TRUE FALSE
Since you can use the "point in polygon" routine, and this apparently isn't already suitably designed to handle the multi-polygon case in R (which I find a bit odd actually), you are left with having to cycle through each of the multiple polygons. Now the trick is, if you are inside an odd number of polygons, you are inside the multi-polygon. If you are inside an even number of polygons, then you are actually outside of the shape.
Point in polygon testing that uses ray-crossings should ALREADY be able to handle this, just by making sure you pass in all the vertices to the original point.in.polygon test, but I am not sure which mechanism R is using, so I can only give you the even/odd advice above.
I also found this code, not sure if it will help:
require(sp)
require(rgdal)
require(maps)
# read in bear data, and turn it into a SpatialPointsDataFrame
bears <- read.csv("bear-sightings.csv")
coordinates(bears) <- c("longitude", "latitude")
# read in National Parks polygons
parks <- readOGR(".", "10m_us_parks_area")
# tell R that bear coordinates are in the same lat/lon reference system
# as the parks data -- BUT ONLY BECAUSE WE KNOW THIS IS THE CASE!
proj4string(bears) <- proj4string(parks)
# combine is.na() with over() to do the containment test; note that we
# need to "demote" parks to a SpatialPolygons object first
inside.park <- !is.na(over(bears, as(parks, "SpatialPolygons")))
# what fraction of sightings were inside a park?
mean(inside.park)
## [1] 0.1720648
# use 'over' again, this time with parks as a SpatialPolygonsDataFrame
# object, to determine which park (if any) contains each sighting, and
# store the park name as an attribute of the bears data
bears$park <- over(bears, parks)$Unit_Name
# draw a map big enough to encompass all points (but don't actually plot
# the points yet), then add in park boundaries superimposed upon a map
# of the United States
plot(coordinates(bears), type="n")
map("world", region="usa", add=TRUE)
plot(parks, border="green", add=TRUE)
legend("topright", cex=0.85,
c("Bear in park", "Bear not in park", "Park boundary"),
pch=c(16, 1, NA), lty=c(NA, NA, 1),
col=c("red", "grey", "green"), bty="n")
title(expression(paste(italic("Ursus arctos"),
" sightings with respect to national parks")))
# now plot bear points with separate colors inside and outside of parks
points(bears[!inside.park, ], pch=1, col="gray")
points(bears[inside.park, ], pch=16, col="red")
# write the augmented bears dataset to CSV
write.csv(bears, "bears-by-park.csv", row.names=FALSE)
# ...or create a shapefile from the points
writeOGR(bears, ".", "bears-by-park", driver="ESRI Shapefile")
I am having a lot of trouble in R calculating the area of home range of an animal. I thought once I produced a home range (if I've done it correctly) calculating the area would be easy, but no
I've pasted some of the code I've been trying. I wonder would anyone have any insight?
# Load package
library(adehabitat)
#Load file Frodo
dd <- read.csv(file.choose(), header = T)
# Plot the home range
xy <- dd[,c("X","Y")]
id <- dd[,"name"]
hr<- mcp(xy,id,percent=95)
plot(hr)
points(xy[xy$id=="frodo",])
#Great. Home range produced. Now calculate area
area <- mcp.area(xy, id,percent = 95),
# Result 2.287789e-09 Ha. Way to small. Maybe it doesnt like Lat / Long.
# Will try and convert coordinates into M or Km
# Load map project
library(mapproj)
x<-mapproject(t$X,t$Y,projection="mercator")
# Its converted it to something but its not M's or Km's.
# I'll try and run it anyway
xy <- x[,c("X","Y")]
# incorrect number of dimensions
# Ill try Project 4
library(proj4)
xy <- dd[,c("X","Y")]
tr <- ptransform(xy/180*pi, '+proj=latlong +ellps=sphere',
'+proj=merc +ellps=sphere')
View(tr)
# There seems to be a Z column filled with 0's.
# It that going to affect anything?
# Let's look at the data
plot(tr)
# Looks good, Lets try and create a home range
xy <- tr[,c("x","y")]
# 'incorrect number of dimensions'
No idea what the problem is. Don't know if I'm on the right track or doing something completely wrong
In order to calculate area you need your points in a projected coordinate systems (area in long/lat would just be units of degree). The type of projection you use is going to have a big effect on the resulting area. For instance the Mercator projection distorts area away from the Equator -- you might want to look into the best equal-area projection for your location. I am going to answer the programming part of your question, once you find the right projections to use you can substitute them in.
require(sp)
require(rgdal)
orig.points <- dd[,c("X","Y")]
# geographic coordinate system of your points
c1 <- CRS("+proj=latlong +ellps=sphere")
# define as SpatialPoints
p1 <- SpatialPoints(orig.points, proj4string=c1)
# define projected coordinate system of your choice, I am using the one you
# defined above, but see:
# http://www.remotesensing.org/geotiff/proj_list/mercator_1sp.html
# to make sure your definition of the mercator projection is appropriate
c2 <- CRS("+proj=merc ellps=sphere")
p2 <- spTransform(p1, c2) # project points
# convert to Polygon (this automatically computes the area as an attribute)
poly <- Polygon(p2)
poly#area #will print out the area
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)