Create square grids and export them as shapefile or table - r

I would like to use the Google Earth Engine in order to extract data for certain countries. I need the data in the form of square grids, so I would like to create those square grids for a certain country, add them to the shapefile and then import the shapefile into the Earth Engine. I already found some code to create the square grids (Create a grid inside a shapefile), but now I have two problems.
First, I need to export the square grids so that I can import them to the Earth Engine. I'm very open to alternatives to the shapefile.
Second, the subsequent code works for some countries (like France), but not for others (like Thailand).
library(raster)
shp = getData(country = "FRA", level = 0)
shp = spTransform(shp, CRSobj = "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0")
plot(shp)
cs = c(10000, 10000)
grdpts = makegrid(shp, cellsize = cs)
spgrd = SpatialPoints(grdpts, proj4string = CRS(proj4string(shp)))
spgrdWithin = SpatialPixels(spgrd[shp,])
plot(spgrdWithin, add = T)
Replacing "FRA" by "THA" in line 2 leads to an error in spTransform.

That fails because you are using utm zone 32. You need to use the zone based on the longitude of the country. You can see them here
You can automate finding a zone with ceiling((longitude+180)/6)
library(raster)
s <- getData(country = "FRA", level = 0)
Get the centroid. In this case you can do
centr <- coordinates(s)
If there are multiple polygons, you can do something like this
centr <- apply(coordinates(s), 2, mean)
Compute the UTM zone. (note that you had 32 for France, which is not good)
zone <- ceiling((centr[1] + 180)/6)
zone
#[1] 31
And then use it like this
crs <- paste0("+proj=utm +datum=WGS84 +unit=m +zone=", zone)
st <- spTransform(s, crs)
For Thailand you would get
s <- getData(country = "THA", level = 0)
centr <- apply(coordinates(s), 2, mean)
zone <- ceiling((centr[1] + 180)/6)
zone
#[1] 47
However, this is not an approach that would work for all countries. UTM zones are 6 degrees wide, and many countries span multiple zones (Russia takes the cake with 28 zones). So depending on your goals, you may want to use another coordinate reference system (crs).
After that, an alternative way to get square polygons is to create a RasterLayer with the extent of s, and a resolution of choice. But I doubt that this is the best way to get data out of GEE. I would suggest uploading the country outline instead.
r <- raster(st, res=10000)
r <- rasterize(st, r, 1)
x <- as(r, "SpatialPolygons")
# write to file
shapefile(x, "test.shp")
# view
plot(x)

Related

How to calculate in KM2 the area of an attribute within randomly selected grid cells in R

I have load the country boundary for Honduras into R and created within it a 2km x 2km grid.
I have then selected 13 random cells from within the grid.
I have then loaded in a vector layer (which is the WWF ecoregions boundaries) which covers the area of Honduras. Within this vector is an attribute called BiomeRealm which describes what type of biomes is present within in polygon of the vector layer (e.g. grasslands or forest).
I have made sure two layers have the same projection.
What I want to do is now extract the area (in KM2) of the attribute BiomeRealm within the 13 random selected cells within the grid I created. My ideal output with would say something like:
BiomeRealm: NT14 - Area - 5km2
NT7 - Area -7km2
How can I do this?
My code so far is:
library(raster)
library(rgdal)
load an admin boudary
shp <- getData(country = "HND", level = 0)
plot(shp)
#transform to a metric coordinate system
shp <- spTransform(shp, CRSobj = "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs
+ellps=WGS84 +towgs84=0,0,0")
plot(shp)
create a grid within this polygon with specified cell size - what cell size is this?
cs <- c(3.28084, 3.28084)*2000 #cell size of 2km by 2km (1 ft = 3.28084 m)
grdpts <- makegrid(shp, cellsize = cs)
Then, we convert this grid (which basically is a matrix of center points) to a SpatialPoints object
spgrd <- SpatialPoints(grdpts, proj4string = CRS(proj4string(shp)))
This can then be converted into a SpatialPixels object. (Note: adding the subset [shp, ] only selects points within the original polygons)
spgrdWithin <- SpatialPixels(spgrd[shp,])
plot(spgrdWithin, add = T)
change to spatial grid
spgrdWithin <- as(spgrdWithin, "SpatialGrid")
now need to select 13 random grids # SAYS SAMPLE FROM GRID, 13 CELLS, RANDOM)
s <- spsample(spgrdWithin, 13, "random")
plot(s)
now read in the WWF data
HNDWWF <- readOGR('.','HONDURAS_WWF')
plot(HNDWWF)
transform HND WWF layer to same coordiantes as grid
HNDWWFTRANS <- spTransform(HNDWWF, CRSobj = "+proj=utm +zone=32 +datum=WGS84 +units=m
+no_defs +ellps=WGS84 +towgs84=0,0,0")
plot(HNDWWFTRANS)
Your question is not very clear. There may be a much better and more direct way to get at what you are really after. So you may want to clarify. But below is how you might select 13 random cells and intersect these with polygons.
If you are going to use UTM for Honduras, then you need to pick the right zone (16)
library(raster)
v <- getData(country = "HND", level = 0)
crs <- "+proj=utm +zone=16 +datum=WGS84 +units=m"
v <- spTransform(v, CRSobj = crs)
Create a raster with the desired resolution
r <- raster(v, res=2000)
r <- init(r, "cell")
# set areas outside Honduras to NA
r <- mask(r, v)
Sample 13 cells
set.seed(1)
s <- sampleRandom(r, 13)
s
# [1] 44486 48219 55229 72480 41917 71715 77681 59055 57573 35633 42037 40820
#[13] 60238
Now see where these are in the "bioregions" (here using adm1)
bio <- getData(country = "HND", level = 1)
bio <- spTransform(bio, crs)
xy <- xyFromCell(r, s)
extract(bio, xy)[, "NAME_1"]
# [1] "Yoro" "Yoro" "Copán"
# [4] "Francisco Morazán" "Yoro" "Francisco Morazán"
# [7] "Valle" "Francisco Morazán" "Francisco Morazán"
#[10] "Colón" "Gracias a Dios" "Yoro"
#[13] "Olancho"
As for the area, given that we have set the resolution to 2000 m, each cell is 4 km2.

Dealing with NA values using extract function in R

I am trying to calculate average annual temperatures for grid cells of 11x11km (except if the cell is coastal, the size is smaller) using the CRU database. The CRS of both vector and raster are the same. However, 332 out of 1363 cells show NA values after the extraction. I want to fill in the NA values before using the dataset for further analysis. Any idea of how I could deal with these missing values? I have looked at several possible solutions on this forum (and others). Unfortunately, none of them don’t seem to apply to my case.
Below are the details of my workflow:
# load the temperature dataset
temp <- brick("/CRU/cru_ts4.02.1901.2017.tmp.dat.nc", varname="tmp")
# set CRS for temp
utm = "+proj=utm +zone=49 +datum=WGS84 +towgs84=0,0,0"
tempro = projectRaster(temp, crs = utm, method = "bilinear")
# load the grid cells (in polygons) & set its CRS
fish <- st_read("/CRU/fish11.shp")
fishpro <- st_transform(fish, "+proj=utm +zone=49 +datum=WGS84 +towgs84=0,0,0")
# extract the temperature dataset
tempgrid <- extract(tempro, fishpro, fun='mean', na.rm=TRUE, df=TRUE, weights = TRUE, small = TRUE,
method='bilinear')
write.csv(tempgrid, file="temp.csv")
whereas the map is:
temperature
I do not think there is a simple answer to your question. Apparently the polygons are not over land; but we cannot tell as we do not have your data. It could also be that the UTM zone chosen is not appropriate.
I can say that what you are doing is wrong. If you need to transform the data; you should transform the vector data, not the raster data (even if that should not affect the NA problem much, if at all).
library(raster)
temp <- brick("/CRU/cru_ts4.02.1901.2017.tmp.dat.nc", varname="tmp")
fish <- st_read("/CRU/fish11.shp")
fishpro <- st_transform(fish, "+proj=longlat +datum=WGS84")
tempgrid <- extract(temp, fishpro, fun='mean', na.rm=TRUE, df=TRUE, small = TRUE)
You could also make a map to see what is going on (and perhaps include that as an image in your quesiton.
x <- crop(temp[[1]], extent(fishpro)+1)
plot(x)
lines(fishpro)

Counting number of points on a raster layer in R

I've got a map with certain number of points on it. I want to (1) calculate the number of points that fall within the raster layer, and (2) extract these points to a data frame.
This is what I've done:
# Packages
library(raster)
library(ggplot2)
library(maptools)
library(tidyverse)
library(dplyr)
library(sp)
# Transform tree ring kml to dataframe
zz<-getKMLcoordinates('treering.kml', ignoreAltitude=TRUE)
l<-as.data.frame(zz)
l<-t(l)
tree <-SpatialPointsDataFrame(l, l,
proj4string = CRS(" +proj=longlat +ellps=WGS84 +datum=WGS84
+no_defs +towgs84=0,0,0"))
# Get world map
data(wrld_simpl)
# Transform World to raster
r <- raster(wrld_simpl, res = 1)
wrld_r <- rasterize(wrld_simpl, r)
# Import permafrost layer to raster
dist1<-raster("PZI.flt")
# Set CRS
dist1 <- projectRaster(from = dist1, crs = CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
+towgs84=0,0,0"))
# Change colours
micolor <- rev(rainbow(12, alpha = 0.35))
transp <- rainbow(12, alpha = 0)
micolor[1:3] <- transp[1]
# Plot all
plot(wrld_r, col = "lightgrey")
plot(dist1, add=TRUE, legend = F, col = micolor)
plot(tree, add=T, pch = 20, col='black', cex=0.2)
I want to calculate and extract black points located on the colorful parts of this map
First raster::projectRaster does not "set" the projection but, rather reprojects the raster given a transformation and resampling. Given the computational requirements of this it is much faster to reproject the point data using sp::spTransform. Once your data is in the same projection space, you can use raster::extract to extract the raster values. Values out side the raster or in nodata (NA) areas will be assigned NA values. You can drop these observations using a simple NA index with which.
It looks like your data may have a constant value outside of the permafrost. Once you identify what this value is (eg., 0) you can remove these points as well. Here is a worked example. First we add packages and create some example data that is similar to yours.
library(sp)
library(raster)
dist1 <- raster(nrow=20, ncol=20)
dist1[] <- sample(1:10, ncell(dist1), replace=TRUE)
dist1[200:400] <- 0
trees <- sampleRandom(dist1, 100, sp=TRUE)
plot(dist1)
plot(trees,pch=20,col="red",add=TRUE)
Now, we extract the raster values and look at the dimensions of the point object (please note that I do not have to use the sp=TRUE argument in the raster::extract function).
trees#data <- data.frame(trees#data, dist1 = extract(dist1, trees))
dim(trees)
Now we create a row index indicating which rows contain zeros, make sure that we have identified rows (using an if statement) and then remove them. Looking at the object dimensions again, we can see how many points were removed from the original point data.
( idx <- which(trees$dist1 %in% 0) )
if(length(idx) > 0) trees <- trees[-idx,]
dim(trees)

Insert points into a grid in R

I have a grid of the map of Porto, I would like to be able to insert in the grid the points to discretize them. In this way to see how many points are in each cell depending on the time.
This is the code of the grid:
#latitudS and longitudS are coordinates x Y
x = longitudS
y = latitudS
df = data.frame(x, y)
# Converting to a Spatial Points Data Frame so you can keep the value that you will change within the Grid
points = SpatialPointsDataFrame(coords = df[, 1:2], df)
# load some spatial data. Administrative Boundary
porto <- getData('GADM', country = 'Portugal', level = 2)
porto$NAME_1
porto <- porto[porto$NAME_2 == "Porto",]
# check the CRS to know which map units are used
proj4string(porto)
# "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
# Create a grid of points within the bbox of the SpatialPolygonsDataFrame
# colorado with decimal degrees as map units
grid <- makegrid(porto, cellsize = 0.003) # cellsize in map units!
# grid is a data.frame. To change it to a spatial data set we have to
grid <- SpatialPoints(grid, proj4string = CRS(proj4string(porto)))
portoWithin <-
SpatialPixels(grid, proj4string = CRS(proj4string(porto)))
#### Converting the GRID to a Raster
ras_portoWithin <- raster(portoWithin)
## Assigning temporary values to the raster
values(ras_portoWithin) <- 0
## Using the Spatial point file created above to replace the values of the raster
r1 <- ras_portoWithin
plot(porto)
plot(portoWithin, add = T)
The idea is that for each cell the number of points will be output depending on the time in which the points have been recorded.
Also ask if there would be any way to transfer that grid to a leaflet, so you can see it on a map.
Thanks!

Buffer (geo)spatial points in R with gbuffer

I'm trying to buffer the points in my dataset with a radius of 100km. I'm using the function gBuffer from the package rgeos. Here's what I have so far:
head( sampledf )
# postalcode lat lon city province
#1 A0A0A0 47.05564 -53.20198 Gander NL
#4 A0A1C0 47.31741 -52.81218 St. John's NL
coordinates( sampledf ) <- c( "lon", "lat" )
proj4string( sampledf ) <- CRS( "+proj=longlat +datum=WGS84" )
distInMeters <- 1000
pc100km <- gBuffer( sampledf, width=100*distInMeters, byid=TRUE )
I get the following warning:
In gBuffer(sampledf, width = 100 * distInMeters, byid = TRUE) :
Spatial object is not projected; GEOS expects planar coordinates
From what I understand/read, I need to change the Coordinate Reference System (CRS),
in particular the projection, of the dataset from 'geographic' to 'projected'.
I'm not sure sure how to change this. These are all Canadian addresses, I might add.
So NAD83 seems to me a natural projection to choose but I may be wrong.
Any/all help would be greatly appreciated.
With a little bit more digging, it turns out that using a 'projected' coordinates reference system is as simple as
# To get Statscan CRS, see here:
# http://spatialreference.org/ref/epsg/3347/
pc <- spTransform( sampledf, CRS( "+init=epsg:3347" ) )
EPSG3347, used by STATSCAN (adequate for Canadian addresses), uses a lambert conformal conic projection. Note that NAD83 is inappropriate: it is a 'geographic', rather than a 'projected' CRS. To buffer the points
pc100km <- gBuffer( pc, width=100*distm, byid=TRUE )
# Add data, and write to shapefile
pc100km <- SpatialPolygonsDataFrame( pc100km, data=pc100km#data )
writeOGR( pc100km, "pc100km", "pc100km", driver="ESRI Shapefile" )
As #MichaelChirico pointed out, projecting your data to usergeos::gBuffer() should be applied with care. I am not an expert in geodesy, but as far I understood from this ESRI article (Understanding Geodesic Buffering), projecting and then applying gBuffer means actually producing Euclidean buffers as opposed to Geodesic ones. Euclidean buffers are affected by the distortions introduced by projected coordinate systems. These distortions might be something to worry about if your analysis involves wide buffers especially with a wider range of latitudes across big areas (I presume Canada is a good candidate).
I came across the same issue some time ago and I targeted my question towards gis.stackexchange - Euclidean and Geodesic Buffering in R. I think the R code that I proposed then and also the given answer are relevant to this question here as well.
The main idea is to make use of geosphere::destPoint(). For more details and a faster alternative, see the mentioned gis.stackexchange link above. Here is my older attempt applied on your two points:
library(geosphere)
library(sp)
pts <- data.frame(lon = c(-53.20198, -52.81218),
lat = c(47.05564, 47.31741))
pts
#> lon lat
#> 1 -53.20198 47.05564
#> 2 -52.81218 47.31741
make_GeodesicBuffer <- function(pts, width) {
# A) Construct buffers as points at given distance and bearing ---------------
dg <- seq(from = 0, to = 360, by = 5)
# Construct equidistant points defining circle shapes (the "buffer points")
buff.XY <- geosphere::destPoint(p = pts,
b = rep(dg, each = length(pts)),
d = width)
# B) Make SpatialPolygons -------------------------------------------------
# Group (split) "buffer points" by id
buff.XY <- as.data.frame(buff.XY)
id <- rep(1:dim(pts)[1], times = length(dg))
lst <- split(buff.XY, id)
# Make SpatialPolygons out of the list of coordinates
poly <- lapply(lst, sp::Polygon, hole = FALSE)
polys <- lapply(list(poly), sp::Polygons, ID = NA)
spolys <- sp::SpatialPolygons(Srl = polys,
proj4string = CRS("+proj=longlat +ellps=WGS84 +datum=WGS84"))
# Disaggregate (split in unique polygons)
spolys <- sp::disaggregate(spolys)
return(spolys)
}
pts_buf_100km <- make_GeodesicBuffer(as.matrix(pts), width = 100*10^3)
# Make a kml file and check the results on Google Earth
library(plotKML)
#> plotKML version 0.5-9 (2019-01-04)
#> URL: http://plotkml.r-forge.r-project.org/
kml(pts_buf_100km, file.name = "pts_buf_100km.kml")
#> KML file opened for writing...
#> Writing to KML...
#> Closing pts_buf_100km.kml
Created on 2019-02-11 by the reprex package (v0.2.1)
And to toy around, I wrapped the function in a package - geobuffer
Here is an example:
# install.packages("devtools") # if you do not have devtools, then install it
devtools::install_github("valentinitnelav/geobuffer")
library(geobuffer)
pts <- data.frame(lon = c(-53.20198, -52.81218),
lat = c(47.05564, 47.31741))
pts_buf_100km <- geobuffer_pts(xy = pts, dist_m = 100*10^3)
Created on 2019-02-11 by the reprex package (v0.2.1)
Others might come up with better solutions, but for now, this worked well for my problems and hopefully can solve other's problems as well.

Resources