6
votes

Basically, I computed a global distribution probability model in the form of ASCII, say: gdpm. gdpm's values are all between 0 and 1.

Then I imported a local map from shape file:

shape <- file.choose()  
map <- readOGR(shape, basename(file_path_sans_ext(shape)))

The next step, I rasterized gdpm, and cropped using the local map:

ldpm <- mask(gdpm, map)

Then, I reclassified this continuous model into a discrete model (I divided the model into 6 levels):

recalc <- matrix(c(0, 0.05, 0, 0.05, 0.2, 1, 0.2, 0.4, 2, 0.4, 0.6, 3, 0.6, 0.8, 4, 0.8, 1, 5), ncol = 3, byrow = TRUE) 

ldpmR <- reclassify(ldpm, recalc)

enter image description here

I've got a cropped and reclassified raster, now I need to summarize land cover, that is, to each level, I want to calculate its proportion of area in each region of the local map. (I don't know how to describe it in terminology). I found and followed an example (RobertH):

ext <- raster::extract(ldpmR, map)

tab <- sapply(ext, function(x) tabulate(x, 10))
tab <- tab / colSums(tab)

But I'm not sure if it works, since the output of tab is confusing. So how to compute land cover area correctly? How can I apply the correct method within each polygon?

My original data is too large, I can only provide an alternative raster (I think this example should apply a different reclassify matrix):

Example raster

Or you can generate a test raster (RobertH):

library(raster)
s <- stack(system.file("external/rlogo.grd", package="raster")) 
writeRaster(s, file='testtif', format='GTiff', bylayer=T, overwrite=T)
f <- list.files(pattern="testtif_..tif")

I also have a question about plotting a raster:

r <- as(r, "SpatialPixelsDataFrame")
r <- as.data.frame(r)
colnames(r) <- c("value", "x", "y")

I do this conversion to make a raster plot-able with ggplot2, is there a more concise method?

2
I've made some clarification, though I don't know the correct terminology.Tianjian Qin

2 Answers

6
votes

loki's answer is OK, but this can be done the raster way, which is safer. And it is important to consider whether the coordinates are angular (longitude/latitude) or planar (projected)

Example data

library(raster)
r <- raster(system.file("external/test.grd", package="raster"))
r <- r / 1000
recalc <- matrix(c(0, 0.05, 0, 0.05, 0.2, 1, 0.2, 0.4, 2, 0.4, 0.6, 3, 0.6, 0.8, 4, 0.8, 2, 5), ncol = 3, byrow = TRUE) 
r2 <- reclassify(r, recalc)

Approach 1. Only for planar data

f <- freq(r2, useNA='no')
apc <- prod(res(r))
f <- cbind(f, area=f[,2] * apc)
f

#     value count    area
#[1,]     1    78  124800
#[2,]     2  1750 2800000
#[3,]     3   819 1310400
#[4,]     4   304  486400
#[5,]     5   152  243200

Approach 2. For angular data (but also works for planar data)

a <- area(r2)
z <- zonal(a, r2, 'sum')
z
#     zone     sum
#[1,]    1  124800
#[2,]    2 2800000
#[3,]    3 1310400
#[4,]    4  486400
#[5,]    5  243200

If you want to summarize by polygons, you can do something like this:

# example polygons
a <- rasterToPolygons(aggregate(r, 25))

Approach 1

# extract values (slow)
ext <- extract(r2, a)

# tabulate values for each polygon
tab <- sapply(ext, function(x) tabulate(x, 5))
# adjust for area (planar data only)
tab <- tab * prod(res(r2))

# check the results, by summing over the regions
rowSums(tab)
#[1]  124800 2800000 1310400  486400  243200    

Approach 2

x <- rasterize(a, r2)
z <- crosstab(x, r2)
z <- cbind(z, area = z[,3] * prod(res(r2)))

Check results:

aggregate(z[, 'area', drop=F], z[,'Var2', drop=F], sum)
  Var2    area
#1    1  124800
#2    2 2800000
#3    3 1310400
#4    4  486400
#5    5  243200

Note that if you are dealing with lon/lat data you cannot use prod(res(r)) to get the cell size. In that case you will need to use the area function and loop over classes, I think.

You also asked about plotting. There are many ways to plot a Raster* object. The basic ones are:

 image(r2)
 plot(r2)
 spplot(r2)

 library(rasterVis); 
 levelplot(r2)

More tricky approaches:

 library(ggplot2) # using a rasterVis method
 theme_set(theme_bw())
 gplot(r2) + geom_tile(aes(fill = value)) +
      facet_wrap(~ variable) +
      scale_fill_gradient(low = 'white', high = 'blue') +
      coord_equal()


 library(leaflet)
 leaflet() %>% addTiles() %>%
 addRasterImage(r2, colors = "Spectral", opacity = 0.8)       
4
votes

Seems like you can get the area by the number of pixels.
Let's start with a reproducible example:

r <- raster(system.file("external/test.grd", package="raster"))
plot(r)

enter image description here

Since, the values in this raster are in another range than your data, let's adapt them to your values:

r <- r / 1000
r[r>1,] <- 1

Afterwards, we apply your reclassification:

recalc <- matrix(c(0, 0.05, 0, 0.05, 0.2, 1, 0.2, 0.4, 2, 0.4, 0.6, 3, 0.6, 0.8, 4, 0.8, 1, 5), ncol = 3, byrow = TRUE) 
r2 <- reclassify(r, recalc)
plot(r2)

enter image description here

Now, how do we get the area?
Since you are working with a projected raster, you can simply use the number of pixels and the raster resolution. Therefore, we first need to check the resolution and the map units of the projection:

res(r)
# [1] 40 40
crs(r)
# CRS arguments:
#   +init=epsg:28992
# +towgs84=565.237,50.0087,465.658,-0.406857,0.350733,-1.87035,4.0812 +proj=sterea
# +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000
# +y_0=463000 +ellps=bessel +units=m +no_defs

Now, we know that we are dealing with pixels of 40 x40 meters, since we have a metric CRS.

Let's use this information to calculate the area of each class.

app <- res(r)[1] * res(r)[2] # area per pixel

table(r2[]) * app
#      1       2       3       4       5 
# 124800 2800000 1310400  486400  243200 

For the plotting of georeferenced rasters, I would like to refer you to an older question here on SO