Applying a function to polygons of a SpatialPolygonsDataFrame in R - r

I am new to working with Spatial Data in R, and I'm having trouble accessing the polygons and relating the polygons to the data. Here's the thing:
I have municipalities a SpatialPolygonsDataFrame, and I can access the data section with municipalities#data this of course is a data frame and I can do the usual manipulation with sapply and similar functions. I want, however, to create a new variable in data that depends on the polygons. So for example, I would like to do:
municipalities$pointInPolygon <- sapply(POLYGON, function(x){getPointInPolygon{x})
or something similar. Thing is, I still don't understand how the data and polygon sections of my SPDF are related and how to access the later.
Any answers,tips, or resources to understand SpatialPolygonsDataFrames better is appreciated.
PS: Here is the data I'm using and how I am reading it
municipalities <- rgdal::readOGR("México_Estados/México_Estados.shp")

Why not use sf package for simple features:
municipalities_sf <- sf::read_sf("México_Estados.shp")
class(municipalities_sf)
#
> [1] "sf" "tbl_df" "tbl" "data.frame"
#
Then you can use dplyr, purrr, etc on data frame directly:
municipalities_sf |>
dplyr::mutate(purrr::map(geometry, function(x){myFantasticFunction}))

Related

How to subset a large SpatialPolygonsDataFrame

I want to calculate the area of a wildfire. I tried this by substracting the NDVI calculated on a Landsat image before and another image after the fire and see where the NDVI was reduced. However, not only in the burning areas the NDVI has changed, but there are also many random differences. I used rasterToPolygons to create a large SpatialPolygonsDataFrame containing all areas where NDVI after - NDVI before < 0.
Now I want to remove all the polygons with an area below a certain threshold value. However, I cannot find a way to subset the large SpatialPolygonsDataFrame.
I found an example on how to get a list of the polygons with an area above the threshold (where burned_poly is the large SpatialPolygonsDataFrame):
pols <- lapply(burned_poly#polygons , slot , "Polygons")
pols_areas <- lapply(pols[[2]], function(x) slot(x, "area"))
However, accessing the large SpatialPolygonsDataFrame like this
bp <- burned_poly#polygons[[1]]#Polygons[pols_areas >= 9000]
gives me a list which I am currently unable to coerce into a SpatialPolygonsDataFrame.
Can someone tell me how to do this last step (I have trouble with the Sf argument of which I don't know what it is in the SpatialPolygonsDataFrame function), or maybe there is a different and better approach to extract the fire extent as a polygon?
Alright, I think I have found a way thanks to Orlandos suggestion to use sf.
I transformed my large SpatialPolygonsDataFrame object to a sf object via st_as_sf() which gave me a multipolygon. This stf_MULTIPOLYGON object can be subdivided into single polygons using st_cast() and the resulting object is subsettable like a data.frame.
bp_sf <- st_as_sf(burned_poly)
bps_sf <- st_cast(bp_sf, "POLYGON")
BpSf <- bps_sf[as.numeric(st_area(bps_sf))>=10000,]
If you are using the simple features sf library you can use functions from the tidyverse. Filtering data is a matter of using the filter() function. Notice that you can convert your objects to sf using st_as_sf(). See: https://r-spatial.github.io/sf/reference/st_as_sf.html and How to filter an R simple features collection using sf methods like st_intersects()?

export khrud object from kernelUD to raster

In R, how can I export a khrud object from function kernelUD in package adehabitat to a raster file (geoTiff)?
I tried following this thread (R: how to create raster layer from an estUDm object) using the code here:
writeRaster(raster(as(udbis1,"SpatialPixelsDataFrame")), "udbis1.tif")
where udbis1 is a khrud object, but I get "Error in as(udbis1, "SpatialPixelsDataFrame") : no method or default for coercing “khrud” to “SpatialPixelsDataFrame."
I think the issue may be that the old thread was before an update to the adehabitat package changed the data format from estUD to khrud. Maybe?
You do not provide a reproducible example. The following works for me:
library(adehabitatHR)
library(raster)
data(puechabonsp)
loc <- puechabonsp$relocs
ud <- kernelUD(loc[, 1])
r <- raster(as(ud[[1]], "SpatialPixelsDataFrame"))
writeRaster(r, filename = file.path(tempdir(), "ud1.tif"))
AdehabitatHR solutions work well for data that are in the required format or when using multiple animals. Though when wanting to create KDE with data organized differently or for only one source, it can be frustrating. For some reason, #johaness' answer doesn't work for my case so here is an alternative solution that avoids the headaches of going into adehabitatHR's innards.
library(adehabitatHR)
library(raster)
# Recreating an example for only one animal
# with a basic xy dataset like one would get from tracking
loc<-puechabonsp$relocs
loc<-as.data.frame(loc)
loc<-loc[loc$Name=="Brock",]
coordinates(loc)<-~X+Y
ud<-kernelUD(loc)
# Extract the UD values and coordinates into a data frame
udval<-data.frame("value" = ud$ud, "lon" = ud#coords[,1], "lat" = ud#coords[,2])
coordinates(udval)<-~lon+lat
# coerce to SpatialPixelsDataFrame
gridded(udval) <- TRUE
# coerce to raster
udr <- raster(udval)
plot(udr)

R-ArcGIS: Impossible to perform dplyr join with data frame?

I've just begun to use the R-ArcGIS bridge package arcgisbinding and am running into a problem when I try to join feature class data with the dplyr package. Here is an example where I'm trying to get the ozone columns from two shapefiles into a single data frame, then export back as a shapefile.
library(dplyr)
library(arcgisbinding)
arc.check_product()
fc <- arc.open(system.file("extdata", "ca_ozone_pts.shp",
package="arcgisbinding"))
d <- arc.select(fc, fields=c('FID', 'ozone'))
p<-arc.select(fc,fields=c('FID', 'ozone'))
p$ozone<-p$ozone*2
p<-left_join(p,d,by="FID")
arc.write(tempfile("ca_new", fileext=".shp"), p)
# original dataframe has shape attributes
str(d)
# new dataframe does not
str(p)
From the arcgisbinding package, p and d above are data frame objects with shape attributes. The problem is that once I use left_join, I lose the spatial attribute data in the joined data frame. Is there a way around this?
So apparently this is a known problem (see GitHub here).
A workaround using the spdplyr package comes by way of Shaun Wallbridge on ESRI GeoNet (link to thread). Basically, convert the arc.data data frame into an sp object, perform analyses, then export as a feature class or shapefile.
library(spdplyr)
library(arcgisbinding)
arc.check_product()
fc <- arc.open(system.file("extdata", "ca_ozone_pts.shp", package="arcgisbinding"))
d <- arc.select(fc,fields=c('FID', 'ozone'))
d.sp <- arc.data2sp(d)
p <-arc.select(fc,fields=c('FID', 'ozone'))
p.sp <- arc.data2sp(p)
p.sp$ozone <- p$ozone*2
joined <- left_join(p.sp, d.sp, by="FID", copy=TRUE)
joined.df <- arc.sp2data(joined)
arc.write(tempfile("ca_ozone_pts_joined", fileext=".shp"), joined.df)

Saving R objects not workspaces

i am completely new in R.
I am trying to save a spatialdataframe and a normal data frame within the same object. when I am applying the code below, it saves the object as a R workspace, is it normal? i mean I wnated to obtain a .rda data instead. What i specifically want to do is to obtain an R data with those two objects. I want the spatialdataframe to keep its spatial charactheristics. Can someone help me?
##import a text table
mcvfinal<-read.csv("dataCPWithAge.csv",header=TRUE,sep=",",dec=".")
##reading the shapefile
library(rgdal) polypc1 <- readOGR(".", "CP3poly_Matchingshp")
##saving the two frames into the same object
save(mcvfinal,polypc1,file="polypc.Rdata")
Try:
saveRDS(list(mcvfinal,polypc1),file="polypc.rds")
Load:
foo = readRDS("polypc.rds")
# mcvfinal is foo[[1]]
# polypc1 is foo[[2]]

How to convert .shp file into .csv in R?

I want to to convert two .shp files into one database that would allow me to draw the maps together.
Also, is there a way to convert .shp files into .csv files? I want to be able to personalize and add some data which is easier for me under a .csv format. What I have in mind if to add overlay yield data and precipitation data on the maps.
Here are the shapefiles for Morocco, and Western Sahara.
Code to plot the two files:
# This is code for mapping of CGE_Morocco results
# Loading administrative coordinates for Morocco maps
library(sp)
library(maptools)
library(mapdata)
# Loading shape files
Mor <- readShapeSpatial("F:/Purdue University/RA_Position/PhD_ResearchandDissert/PhD_Draft/Country-CGE/MAR_adm1.shp")
Sah <- readShapeSpatial("F:/Purdue University/RA_Position/PhD_ResearchandDissert/PhD_Draft/Country-CGE/ESH_adm1.shp")
# Ploting the maps (raw)
png("Morocco.png")
Morocco <- readShapePoly("F:/Purdue University/RA_Position/PhD_ResearchandDissert/PhD_Draft/Country-CGE/MAR_adm1.shp")
plot(Morocco)
dev.off()
png("WesternSahara.png")
WesternSahara <- readShapePoly("F:/Purdue University/RA_Position/PhD_ResearchandDissert/PhD_Draft/Country-CGE/ESH_adm1.shp")
plot(WesternSahara)
dev.off()
After looking into suggestions from #AriBFriedman and #PaulHiemstra and subsequently figuring out how to merge .shp files, I have managed to produce the following map using the following code and data (For .shp data, cf. links above)
code:
# Merging Mor and Sah .shp files into one .shp file
MoroccoData <- rbind(Mor#data,Sah#data) # First, 'stack' the attribute list rows using rbind()
MoroccoPolys <- c(Mor#polygons,Sah#polygons) # Next, combine the two polygon lists into a single list using c()
summary(MoroccoData)
summary(MoroccoPolys)
offset <- length(MoroccoPolys) # Next, generate a new polygon ID for the new SpatialPolygonDataFrame object
browser()
for (i in 1: offset)
{
sNew = as.character(i)
MoroccoPolys[[i]]#ID = sNew
}
ID <- c(as.character(1:length(MoroccoPolys))) # Create an identical ID field and append it to the merged Data component
MoroccoDataWithID <- cbind(ID,MoroccoData)
MoroccoPolysSP <- SpatialPolygons(MoroccoPolys,proj4string=CRS(proj4string(Sah))) # Promote the merged list to a SpatialPolygons data object
Morocco <- SpatialPolygonsDataFrame(MoroccoPolysSP,data = MoroccoDataWithID,match.ID = FALSE) # Combine the merged Data and Polygon components into a new SpatialPolygonsDataFrame.
Morocco#data$id <- rownames(Morocco#data)
Morocco.fort <- fortify(Morocco, region='id')
Morocco.fort <- Morocco.fort[order(Morocco.fort$order), ]
MoroccoMap <- ggplot(data=Morocco.fort, aes(long, lat, group=group)) +
geom_polygon(colour='black',fill='white') +
theme_bw()
Results:
New Question:
1- How to eliminate the boundaries data that cuts though the map in half?
2- How to combine different regions within a .shp file?
Thanks you all.
P.S: the community in stackoverflow.com is wonderful and very helpful, and especially toward beginners like :) Just thought of emphasizing it.
Once you have loaded your shapefiles into Spatial{Lines/Polygons}DataFrames (classes from the sp-package), you can use the fortify generic function to transform them to flat data.frame format. The specific functions for the fortify generic are included in the ggplot2 package, so you'll need to load that first. A code example:
library(ggplot2)
polygon_dataframe = fortify(polygon_spdf)
where polygon_spdf is a SpatialPolygonsDataFrame. A similar approach works for SpatialLinesDataFrame's.
The difference between my solution and that of #AriBFriedman is that mine includes the x and y coordinates of the polygons/lines, in addition to the data associated to those polgons/lines. I really like visualising my spatial data with the ggplot2 package.
Once you have your data in a normal data.frame you can simply use write.csv to generate a csv file on disk.
I think you mean you want the associated data.frame from each?
If so, it can be accessed with the # slot access function. The slot is called data:
write.csv( WesternSahara#data, file="/home/wherever/myWesternSahara.csv")
Then when you read it back in with read.csv, you can try assigning:
myEdits <- read.csv("/home/wherever/myWesternSahara_modified.csv")
WesternSahara#data <- myEdits
You may need to do some massaging of row names and so forth to get it to accept the new data.frame as valid. I'd probably try to merge the existing data.frame with a csv you read in in R, rather than making edits destructively....

Resources