Draw polygon from raster after occurrence modeling - r

I want to draw polygons for species occurrence using the same methods BIEN uses, so I can use both my polygons and theirs. They use Maxent to model species occurrence when they have more then occurrence points.
So, this is, for example, a BIEN polygon:
library(BIEN)
Mormolyca_ringens<- BIEN_ranges_load_species(species = "Mormolyca ringens")
#And this is a polygon, yes. A SpatialPolygonsDataFrame.
plot(wrld_simpl, xlim=c(-100,-40), ylim=c(-30,30), axes=TRUE,col="light yellow", bg="light blue")
plot(Mormolyca_ringens, col="green", add=TRUE)
Mormolyca ringens polygon
Ok, then I'm trying to draw my polygons because BIEN lacks some for species I need.
# first, you need to download the Maxent software here: http://biodiversityinformatics.amnh.org/open_source/maxent/
#and paste the "maxent.jar" file in the ’java’ folder of the ’dismo’ package, which is here:
system.file("java", package="dismo")
#You have to do this **before** loading the libraries
#install.packages("rJava")
library(rJava)
#If you get the message that cannot load this library, it's possible that your version of java is not 64bit.
#Go to Oracle and install Java for windows 64bit.
#If library still doesn't load: Look in your computer for the path where the java's jre file is and paste in the code below
Sys.setenv(JAVA_HOME="your\\path\\for\\jre") #mine is "C:\\Program Files\\Java\\jre1.8.0_144", for example
library(rJava)
library(dismo)
library(maptools)
#Giving credits: I wrote the following code based on this tutorial: https://cran.r-project.org/web/packages/dismo/vignettes/sdm.pdf
#Preparing the example data - the map
data(wrld_simpl)
ext = extent(-90, -32, -33, 23)
#Preparing the example data - presence data for Bradypus variegatus
file <- paste(system.file(package="dismo"), "/ex/bradypus.csv", sep="")
bradypus <- read.table(file, header=TRUE, sep=',')
bradypus <- bradypus[,-1] #don't need th first col
#Getting the predictors (the variables)
files <- list.files(path=paste(system.file(package="dismo"),
'/ex', sep=''), pattern='grd', full.names=TRUE )
predictors <- stack(files)
#making a training and a testing set.
group <- kfold(bradypus, 5)
pres_train <- bradypus[group != 1, ]
pres_test <- bradypus[group == 1, ]
#Creating the background
backg <- randomPoints(predictors, n=1000, ext=ext, extf = 1.25)
colnames(backg) = c('lon', 'lat')
group <- kfold(backg, 5)
backg_train <- backg[group != 1, ]
backg_test <- backg[group == 1, ]
# Running maxent
xm <- maxent(predictors, pres_train, factors='biome')
plot(xm)
#A response plot:
response(xm)
# Evaluating and predicting
e <- evaluate(pres_test, backg_test, xm, predictors)
px <- predict(predictors, xm, ext=ext, progress='text', overwrite=TRUE)
#Checking result of the prediction
par(mfrow=c(1,2))
plot(px, main='Maxent, raw values')
plot(wrld_simpl, add=TRUE, border='dark grey')
tr <- threshold(e, 'spec_sens')
plot(px > tr, main='presence/absence')
plot(wrld_simpl, add=TRUE, border='dark grey')
points(pres_train, pch='+')
At this point, I have the following image:
Prediction for example's occurrence
And I'm trying to make a polygon from this raster with this code:
predic_pol<-rasterToPolygons(px )
And also:
px_rec<-reclassify(px, rcl=0.5, include.lowest=FALSE)
px_pol<-rasterToPolygons(px_rec)
But i keep getting a pixels version of my extent
Can you please give me a hint so I can extract a polygon out of this raster, like the BIEN's one? (Also I'm new to modeling and to R... any tips are welcome)
EDIT: this is the px console output:
> px
class : RasterLayer
dimensions : 172, 176, 30272 (nrow, ncol, ncell)
resolution : 0.5, 0.5 (x, y)
extent : -120, -32, -56, 30 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : C:\Users\thai\Documents\ORCHIDACEAE\Ecologicos\w2\predictions\Trigonidiumobtusum_prediction.grd
names : layer
values : 6.705387e-06, 0.9999983 (min, max)
Thank you in advance
Edit 2: Solution
Thanks to #Val I got to this:
#Getting only the values>tr to make the polygon
#"tr" is what gives me the green raster instear of the multicolour one
pol <- rasterToPolygons(px>tr,function(x) x == 1,dissolve=T)
#Ploting
plot(wrld_simpl, xlim=c(-120,-20), ylim=c(-60,10), axes=TRUE,col="light yellow", bg="light blue")
plot(pol, add=T, col="green")
And now I have what I wanted! Thank you!
(The polygon is not the same in the figures only because I used a different data set I had at my environment at the moment I got #Val 's answer)
Bonus question:
Do you know how to smooth the edges so I get a non pixelized polygon?

I don't know BIEN, so I din't really look at this part of your example. I just generalized your problem/question down to the following:
You have a binary raster (with 0 for absence and 1 for presence) and you want to convert all areas with 1 to a polygon.
As for your px raster, it's a bit odd that your values are not 0 and 1 but more basically 0 and basically 1. But if that's a problem, that can be an easy fix.
So I tried to recreate your example with just the area of Brasil:
library(raster)
library(rgeos)
# get Brasil borders
shp <- getData(country = 'BRA',level=0)
#create binary raster
r <- raster(extent(shp),resolution=c(0.5,0.5))
r[] <- NA # values have to be NA for the buffering
# take centroid of Brasil as center of species presence
cent <- gCentroid(shp)
# set to 1
r[cellFromXY(r,cent)] <- 1
# buffer presence
r <- buffer(r,width=1000000)
# set rest 0
r[is.na(r)] <- 0
# mask by borders
r <- mask(r,shp)
This is close enough to your raster I guess:
So now to the conversion to the polygon:
pol <- rasterToPolygons(r,function(x) x == 1,dissolve=T)
I use a function to only get pixels with value 1. Also I dissolve the polygons to not have single pixel polygons but rather an area. See rasterToPolygons for other options.
And now plot the borders and the new polygon together:
plot(shp)
plot(pol,col='red',add=T)
And there you have it, a polygon of the distribution. This is the console output:
> pol
class : SpatialPolygonsDataFrame
features : 1
extent : -62.98971, -43.48971, -20.23512, -1.735122 (xmin, xmax, ymin, ymax)
coord. ref. : NA
variables : 1
names : layer
min values : 1
max values : 1
Hope that helps!
Edit: Bonus answer
You have to be clear, that the pixelized boundaries of your polygon(s) represent an accurate representation of your data. So any change to that means a loss of precision. Now, depending on your purpose, that might not matter.
There's multiple ways to achieve it, either at the raster side with disaggregating and smoothing/filtering etc. or at the polygon side, where you can apply specific filters to the polygons like this.
If it's purely aesthetic, you can try gSimplify from the rgeos package:
# adjust tol for smoothness
pol_sm <- gSimplify(pol,tol=0.5)
plot(pol)
lines(pol_sm,col='red',lwd=2)

Related

How can I subset a raster by conditional statement in R using `terra`?

I am trying to plot only certain values from a categorical land cover raster I am working with. I have loaded it in to R using the terra package and it plots fine. However, since the original data did not come with a legend, I am trying to find out which raster value corresponds to what on the map.
Similar to the answer provided here: How to subset a raster based on grid cell values
I have tried using the following line:
> landcover
class : SpatRaster
dimensions : 20057, 63988, 1 (nrow, ncol, nlyr)
resolution : 0.0005253954, 0.0005253954 (x, y)
extent : -135.619, -102, 59.99989, 70.53775 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (EPSG:4326)
source : spat_n5WpgzBuVAV3Ijm.tif
name : CAN_LC_2015_CAL_wgs
min value : 1
max value : 18
> plot(landcover[landcover == 18])
Error: cannot allocate vector of size 9.6 Gb
However, this line takes a very long time to run and produces a vector memory error. The object is 1.3 kb in the global environment and the original tif is about 300 mb.
You can use cats to find out which values correspond to which categories.
library(terra)
set.seed(0)
r <- rast(nrows=10, ncols=10)
values(r) <- sample(3, ncell(r), replace=TRUE) - 1
cls <- c("forest", "water", "urban")
levels(r) <- cls
names(r) <- "land cover"
cats(r)[[1]]
# ID category
#1 0 forest
#2 1 water
#3 2 urban
To plot a logical (Boolean) layer for one category, you can do
plot(r == "water")
And from from the above you can see that in this case that is equivalent to
plot(r == 1)
I think I found the solution to write the conditional within the plot function as below:
plot(landcover == 18)
For those looking for a reproduceable example, just load the rlogo:
s <- rast(system.file("ex/logo.tif", package="terra"))
s <- s$red
plot(s == 255)

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)

How to get values for a pixel from a geoTIFF in R?

I'm trying to get RGB components from a geoTIFF file in R. The colours on the image correspond to different land classification types and I have a legend for each classification type in RGB components.
I'm using the raster library. My code so far is
library(raster)
my.map = raster("mygeoTIFFfile.tif")
Here is the information on the file once it has been read in:
> my.map[[1]]
class : RasterLayer
dimensions : 55800, 129600, 7231680000 (nrow, ncol, ncell)
resolution : 0.002777778, 0.002777778 (x, y)
extent : -180.0014, 179.9986, -64.99861, 90.00139 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : filepah/filename.tif
names : filename.tif
values : 11, 230 (min, max)
The specific geoTIFF file I'm working on can be found here:
http://due.esrin.esa.int/page_globcover.php
(just click on "Globcover2009_V2.3_Global_.zip")
Can someone please help me get the value from a single pixel location from this file please?
The rasterToPoints() function will convert your raster data to a matrix containing x, y, and value for each point. This will be very large, but may be what you're looking for if you want to do a broad analysis of the data.
library(raster)
map <- raster("GLOBCOVER_L4_200901_200912_V2.3.tif")
data <- rasterToPoints(map, progress="text")
head(data)
Another option is to use the extract() function to return a single point by passing a SpatialPoints object with latitude/longitude. If you only want a few individual data points, this will be a lot faster than loading the entire thing into a matrix.
library(raster)
map <- raster("GLOBCOVER_L4_200901_200912_V2.3.tif")
extract(map, SpatialPoints(cbind(-123.3680884, 48.4252848)))
It seems that you are asking the wrong question.
To get a value for a single pixel (grid cell), you can do use indexing. For example, for cell number 10,000 and 10,001 you can do r[10000:10001].
You could get all values by doing values(r). But that will fail for a very large raster like this (unless you have lots of RAM).
However, the question you need answered, it seems, is how to make a map by matching integer cell values with RGB colors.
Let's set up an example raster
library(raster)
r <- raster(nrow=4, ncol=4)
values(r) <- rep(c(11, 14, 20, 30), each=4)
And some matching RGB values
legend <- read.csv(text="Value,Label,Red,Green,Blue
11,Post-flooding or irrigated croplands (or aquatic),170,240,240
14,Rainfed croplands,255,255,100
20,Mosaic cropland (50-70%) / vegetation (grassland/shrubland/forest) (20-50%),220,240,100
30,Mosaic vegetation (grassland/shrubland/forest) (50-70%) / cropland (20-50%) ,205,205,102")
Compute the color code
legend$col <- rgb(legend$Red, legend$Green, legend$Blue, maxColorValue=255)
set up a "color table"
# start with white for all values (1 to 255)
ct <- rep(rgb(1,1,1), 255)
# fill in where necessary
ct[legend$Value+1] <- legend$col
colortable(r) <- ct
plot
plot(r)
You can also try:
tb <- legend[, c('Value', 'Label')]
colnames(tb)[1] = "ID"
tb$Label <- substr(tb$Label, 1,10)
levels(r) <- tb
library(rasterVis)
levelplot(r, col.regions=legend$col, at=0:length(legend$col))

Getting Data out of raster file in R

I'm new to raster files, but they seem to be the best way to open up the large gov't files that have all the weather data, so I'm trying to figure out how to use them. For reference, I'm downloading the files located here (just some run of the mill weather stuff). When I use the raster package of R to import the file like this
> r <- raster("/path/to/file.grb")
Everything works fine. I can even get a little metadata when I type in
> r
class : RasterLayer
band : 1 (of 37 bands)
dimensions : 224, 464, 103936 (nrow, ncol, ncell)
resolution : 0.125, 0.125 (x, y)
extent : -125.0005, -67.0005, 25.0005, 53.0005 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +a=6371200 +b=6371200 +no_defs
data source : /path/to/file.grb
names : NLDAS_MOS0125_H.A20140629.0100.002
All I've managed to do at this point is index the raster in a very obvious way.
> r[100,100]
267.1
So, I guess I can "index" it, but I have no idea what the number 267.1 means. It's certainly not all there is in the cell. There should be a bunch of variables including, but not limited to, soil moisture, surface runoff, and evaporation.
How can I access this information in the same way using R?
# create two rasters
r1 <- raster(matrix(ncol = 10, nrow = 10, runif(100)))
r2 <- raster(matrix(ncol = 10, nrow = 10, runif(100)))
# creates a raster stack -- the stack (or brick function) allows you to
# to use multilayer band rasters
# http://www.inside-r.org/packages/cran/raster/docs/stack
st_r <- stack(r1, r2)
# extract values -- will create a matrix with 100 rows and two columns
vl <- getValues(st_r)
r <- raster("/path/to/file.grb")
values <- getValues(r)
You can read about the function here:
http://www.inside-r.org/packages/cran/raster/docs/values
I believe that the problem is that you are using raster and not stack. The raster function results in a single layer (matrix) whereas stack or brick read an array with all of the raster layers. Here is an example that demonstrates extracting values using an [i,j,z] index.
library(raster)
setwd("D:/TMP")
download.file("ftp://hydro1.sci.gsfc.nasa.gov/data/s4pa/NLDAS/NLDAS_MOS0125_H.002/2014/180/NLDAS_MOS0125_H.A20140629.0000.002.grb",
destfile="NLDAS_MOS0125_H.A20140629.0000.002.grb", mode="wb")
r <- stack("NLDAS_MOS0125_H.A20140629.0000.002.grb")
names(r) <- paste0("L", seq(1:nlayers(r)))
class(r)
# Values for [i,j]
i=100
j=100
r[i,j]
# Values for i,j and z at layer(s) 1, 5 and 10
z=c(1,5,10)
r[i,j][z]

How to create a raster brick with rasters of different extents?

I am new in R so this question is very basic but I have been struggling with it and could not find a solution that worked. I want to create a raster brick from some landsat images of the same area.
They were downloaded in HDF-EOS format, and I used the Modis Reprojection Tool to convert them to .tif.
The resulting rasters have the same projection, but differ in their extent, resolution and origin.
I tried several approaches, summarized here below:
defining a subset extent manually and subsetting all the rasters. Then trying to make a brick with the subset rasters
Resampling the rasters, to give them the same number of columns and rows. Ideally that would ensure the raster cells are aligned and can be put into a raster brick. This option created a brick where rasters had no values, they were empty.
I am wondering what is the concept I should follow to correct the extent. Would it be correct (and efficient) to create an empty raster that I would fill in later with the values of the imported landsat image? Can you see where I am making a mistake?
In case it is relevant, I am working on a Mac OSX Version 10.9.1, and using rgdal version 0.8-14
Any help will be very appreciated!
Thankyou
I add here the code I have been using:
# .tif files have been creating using the Modis Reprojection Tool. Input
# files used for this Tool was LANDSAT HDF-EOS imagery.
library(raster)
library(rgdal)
setwd()=getwd()
# Download the files from dropbox:
dl_from_dropbox <- function(x, key) {
require(RCurl)
bin <- getBinaryURL(paste0("https://dl.dropboxusercontent.com/s/", key, "/", x),
ssl.verifypeer = FALSE)
con <- file(x, open = "wb")
writeBin(bin, con)
close(con)
message(noquote(paste(x, "read into", getwd())))
}
dl_from_dropbox("lndsr.LT52210611985245CUB00-vi.NDVI.tif", "qb1bap9rghwivwy")
dl_from_dropbox("lndsr.LT52210611985309CUB00-vi.NDVI.tif", "sbhcffotirwnnc6")
dl_from_dropbox("lndsr.LT52210611987283CUB00-vi.NDVI.tif", "2zrkoo00ngigfzm")
# Create three rasters
tif1 <- "lndsr.LT52210611985245CUB00-vi.NDVI.tif"
tif2 <- "lndsr.LT52210611985309CUB00-vi.NDVI.tif"
tif3 <- "lndsr.LT52210611987283CUB00-vi.NDVI.tif"
r1 <- raster(tif1, values=TRUE)
r2 <- raster(tif2, band=1, values=TRUE)
r3 <- raster(tif3, band=1, values=TRUE)
### Display their properties
# projection is identical for the three rasters
projection(r1)
# "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
projection(r2)
projection(r3)
# Extents are different
extent(r1)
# class : Extent
# xmin : -45.85728
# xmax : -43.76855
# ymin : -2.388705
# ymax : -0.5181549
extent(r2)
# class : Extent
# xmin : -45.87077
# xmax : -43.78204
# ymin : -2.388727
# ymax : -0.5208711
extent(r3)
# class : Extent
# xmin : -45.81952
# xmax : -43.7173
# ymin : -2.405129
# ymax : -0.5154312
# origin differs for all
origin(r1)
# 5.644590e-05 -8.588605e-05
origin(r2)
# 0.0001122091 -0.0001045107
origin(r3)
# 6.949976e-05 -5.895945e-05
# resolution differs for r2
res(r1)
# 0.0002696872 0.0002696872
res(r2)
# 0.0002696875 0.0002696875
res(r3)
# 0.0002696872 0.0002696872
## Try different approaches to create a raster brick
# a- define a subset extent, and subset all the rasters
plot(r1, main="layer1 NDVI")
de <- drawExtent(show=TRUE, col="red")
de
# class : Extent
# xmin : -45.36159
# xmax : -45.30108
# ymin : -2.002435
# ymax : -1.949501
e <- extent(-45.36159,-45.30108,-2.002435,-1.949501)
# Crop each raster with this extent
r1c <- crop(r1,e)
r2c <- crop(r2,e)
r3c <- crop(r3,e)
# Make raster brick
rb_a <- brick(r1c,r2c,r3c)
# Error in compareRaster(x) : different extent
# b- Resample each raster
s <- raster(nrow=6926, ncol=7735) # smallest nrow and ncol among r1,r2 and r3
r1_res <- resample(r1,s, method="ngb")
r2_res <- resample(r2,s, method="ngb")
r3_res <- resample(r3,s, method="ngb")
# Resampling gives for the three rasters the following message:
# Warning message:
# In .local(x, y, ...) :
# you are resampling y a raster with a much larger cell size,
# perhaps you should use "aggregate" first
# Make raster brick
rb_c <- brick(r1, r2, r3)
# Error in compareRaster(x) : different extent
here are some things to help you out. Since I don't have your .tif files just some hints. Have you checked the extent on your raster s? It's the size of the world, with just those columns its cells are extremely large. So you have to add an extent to your raster before resampling it. From your info I did something like this:
# create an extent that includes all your data
e<-extent(-46, -43, -2, -0.6)
# create a raster with that extent, and the number of rows and colums to achive a
# similar resolution as you had before, you might have to do some math here....
# as crs, use the same crs as in your rasters before, from the crs slot
s<-raster(e, nrows=7000, ncols=7800, crs=r1#crs)
# use this raster to reproject your original raster (since your using the same crs,
# resample should work fine
r1<-resample(r1, s, method="ngb")
Happy Holidays,
Ben
PS a better way to find your desired extent & resolution:
# dummy extent from your rasters, instead use lapply(raster list, extent)
a<-extent(-45.85728, -43.76855, -2.388705, -0.5181549)
b<-extent(-45.87077, -43.78204, -2.388727, -0.5208711)
c<-extent(-45.81952 ,-43.7173 , -2.405129 ,-0.5154312)
extent_list<-list(a, b, c)
# make a matrix out of it, each column represents a raster, rows the values
extent_list<-lapply(extent_list, as.matrix)
matrix_extent<-matrix(unlist(extent_list), ncol=length(extent_list)
rownames(matrix_extent)<-c("xmin", "ymin", "xmax", "ymax")
# create an extent with the extrem values of your extent
best_extent<-extent(min(matrix_extent[1,]), max(matrix_extent[3,]),
min(matrix_extent[2,]), max(matrix_extent[4,]))
# the range of your extent in degrees
ranges<-apply(as.matrix(best_extent), 1, diff)
# the resolution of your raster (pick one) or add a desired resolution
reso<-res(r1)
# deviding the range by your desired resolution gives you the number of rows and columns
nrow_ncol<-ranges/reso
# create your raster with the following
s<-raster(best_extent, nrows=nrow_ncol[2], ncols=nrow_ncol[1], crs=r1#crs)

Resources