1

As I have only recently started using R for spatial analysis, and I am not a geographer or spatial data specialist by any means, I have a -what I presume to be- relatively simple question. I am trying to calculate the area of part of a stacked raster object that meets certain conditions. More specifically, from a dataset from the deep sea in the south Atlantic, I have stacked two raster objects (depth and slope) that are further identical in coordinate system (WGS84) and x-y (Lat-Long) position. From the stacked raster object, I would like to extract the part that sits between (say) 1000 and 4000 m depth, with a slope of more than 10 degrees. I would like to know what the areal extent is in square km and I would like to add it to a previously plotted map. Below is a reproducible example:

# Raster object containing depth values
dpt <-  raster(ncol=623, nrow=815, xmx=-31.72083, xmn=-38.50417,
ymn=-33.8875, ymx=-28.70417)
values(dpt) <- sample(-200:-5000, size=(nrow(dpt)*ncol(dpt)), replace=T)

# Raster object containing slope values
slp <- raster(ncol=623, nrow=815, xmx=-31.72083, xmn=-38.50417,
ymn=-33.8875, ymx=-28.70417)
values(slp) <- sample(0:30, size=(nrow(slp)*ncol(slp)), replace=T)

# Stack raster objects
stk <- stack(dpt,slp)


 # Colour palette
 colrs <- colorRampPalette(c("navyblue","dodgerblue3","cyan2","green2","darkgoldenrod1"))
 # Plot raster map; does not look like ocean floor because of "sample"
 plot(dpt, xlab="Longitude", ylab="Latitude", col=colrs(100), font.lab=2,
 cex.lab=1.5, las=1)


 # Create a blank copy of previous raster plot
 selectAtt <- raster(dpt)
 # Fill in cells where Attribute(s) meet(s) conditions
 selectAtt[stk$layer.1 <= -1000 & stk$layer.1 >= -4000 & stk$layer.2 >=
 10] <- 90
 # Set object projection
 projection(selectAtt) <- CRS("+proj=longlat +ellps=WGS84")
 # Plot selection in previous raster
 plot(selectAtt, col="red", add=TRUE, legend=F, proj4string=crswgs84)

Then, my question would be: what is the area (within total area) with both depth (layer.1) and slope (layer.2) meeting given conditions ? In this case, with elevation between -1000 and -4000 m and with a slope angle of >10 degrees.

My initial thought was to do:

> area(selectAtt)

giving the answer:

class       : RasterLayer 
dimensions  : 623, 815, 507745  (nrow, ncol, ncell)
resolution  : 0.008323108, 0.008319957  (x, y)
extent      : -38.50417, -31.72083, -33.8875, -28.70417  (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0 
data source : in memory
names       : layer 
values      : 0.7083593, 0.7481782  (min, max)

Which is the basic information about the Raster object...it gave me the strange sensation that I was not getting the answer to the question I posed. Maybe I was not asking the correct question ? In any case, it did not tell me anything about the size of the area meeting my conditions.

Then I did:

a <- stk[stk$layer.1 <= -1000 & stk$layer.1 >= -4000 & stk$layer.2 >= 10]
 area(a, na.rm=T)

 # This gives me the error message:
 Error in (function (classes, fdef, mtable)  :
         unable to find an inherited method for function ‘area’ for signature ‘"matrix"’

I have tried to find what this actually means, and it appears to be a mismatch between S3 and S4 functionalities, even though I don't exactly know what those are.

Anyhow, I thought I was posing a relatively simple query to the spatial data, namely what is the area corresponding to a selection based on information from several layers from a raster stack ? What am I missing here ? Any help is greatly appreciated !

lbusett
  • 5,801
  • 2
  • 24
  • 47

3 Answers3

1

There are several methods how to access the layers and the values of a raster stack. I prefer the following:

#select first layer of raster stack 
stk[[1]]

#get values of second layer
stk[[2]][]

Now back to your question:

When I want to calculate the area of pixels that meet certain criteria, I do the following (when working with small rasters):

numberOfPixels <- sum(stk[[1]][] <= -1000 & stk[[1]][] >= -4000 & stk[[2]][] >= 10, na.rm=T)

This gives you the number of pixels that meet the defined criteria. If you would be working in a projected coordinate system (you are working in WGS 84 and therefore you can't calculate accurate areas from this) you would simply multiply the numberOfPixels by the resolution of your raster:

area <- numberOfPixels * (res(stk)[1] * res(stk)[2])

If you want to get the area in squared meters, reproject your raster to a projected coordinate system. For example into UTM. In your case this one might be a good fit (note that your extent is stretching across multiple zones): http://www.spatialreference.org/ref/epsg/wgs-84-utm-zone-24s/

stk <- projectRaster(from=stk, crs="+proj=utm +zone=24 +south +ellps=WGS84 +datum=WGS84 +units=m +no_defs")

Then again:

numberOfPixels <- sum(stk[[1]][] <= -1000 & stk[[1]][] >= -4000 & stk[[2]][] >= 10, na.rm=T)
area <- numberOfPixels * (res(stk)[1] * res(stk)[2])
area
[1] 258898897920 
maRtin
  • 6,336
  • 11
  • 43
  • 66
  • Hi maRtin, thanks for your reply and your clear explanation, that helps a lot in understanding the generics of it all. However, I have a counter-question: I got an alternative answer from another forum/list, where the person advised me to simply do the following to obtain the area corresponding to the given conditions: `area_raster <- area(selectAtt, na.rm = TRUE); cellStats(area_raster, 'sum')` However, this gives me a different outcome. What are the differences in approaches, and which is the one to use best ? – Francesc Montserrat Feb 24 '17 at 19:38
  • As the area function help page states: `Compute the approximate surface area of cells in an unprojected (longitude/latitude) Raster object. ... This variation is greatest near the poles and the values are thus not very precise for very high latitudes.` Apparently both methods yield in substantially different results. I would however argue that you are safer off, if you first reproject the raster into a projected coordinate system and then calculate the area. (Note again: A single UTM Zone might not be the best approach, since your Area of Interest is quite large). – maRtin Feb 24 '17 at 21:20
  • 1
    I don't know if this is the (only) issue, here. If I didn't do anything wrong, the results are **completely different**: `maRtin approach`: 258898897920 - `cellStats approach`: 156745.9. Even if it was that "measure units" were different, it would't explain the difference. Also, the area is not so near to the poles to explain so large an "error" for the `cellStats` approach, nor so "big" (about 600 km) to justify vast deviations due to the "single UTM zone" problem. I rather agree with @maRtin that the reprojection approach seems to be better, but maybe we're still missing something. – lbusett Feb 24 '17 at 21:57
0

Ok, here is a possible different approach, based on converting the raster cells to polygons and then computing the area of the polygons exploiting package geosphere:

require(geosphere)
polys = rasterToPolygons(stk)
polys_sub = subset(polys, (layer.1 <= -1000 & layer.1 >= -4000 & layer.2 >= 10))
> sum(areaPolygon(polys_sub))/1E6  #in km2

[1] 157035.2

This is quite similar with what the OP got from the cellStats method (about 2% off), and still completely different from the reprojection method. From what I understand from the help of areaPolygon this should be a numerically correct approach. I still don't understand the different results with respect to @maRtin answer, though.

By the way, for the OP: why are you setting-up an example with a so "strange" reference system (lat/long, different x/y resolution) ?

Community
  • 1
  • 1
lbusett
  • 5,801
  • 2
  • 24
  • 47
  • Can I ask the reason for the downvote ? Is this approach wrong ? – lbusett Mar 08 '17 at 15:01
  • This doesn't answer the question of calculating area from a raster. – SeldomSeenSlim Mar 08 '17 at 16:33
  • I disagree. What we want to compute is the area corresponding to the cells of a raster **on the surface that that they are used to represent**. Cells of a raster (or a subset of it) in geo projection represent a specific portion of earth's surface delimited by boundaries of the cells, which area is computable. However, since the cells are not square and the units are not metric some tricks are needed to compute the area (converting projection, or using a vector representation and spherical geometry). So, it's possible that the answer is wrong, but it does correctly answer the question. – lbusett Mar 08 '17 at 21:26
  • The question is specifically *"Calculate selected area from stacked raster object"*. If one is concerned with how a raster object relates to a surface, that a wholly separate issue. Clearly the person asking this question has issue that are bigger than just being able to calculate an area. The question here is specific to rasters, and you don't need a coordinate system to calculate the area of a raster occupied by a given class of pixel. – SeldomSeenSlim Mar 08 '17 at 21:43
  • I would have to say that @LoBu is closer to the mark here. My issue was indeed, as (s)he phrased it: "Cells of a raster (or a subset of it) in geo projection represent a specific portion of earth's surface delimited by boundaries of the cells, which area is computable.". IMO, projection starts to play a role once you want to express it in a unit system (farthings, m, nautical miles). I will go back to my data and try out the approaches mentioned here. Many thanks ! – Francesc Montserrat Mar 10 '17 at 17:50
  • @LoBu, I am not sure what you mean with your earlier question of "why are you setting-up an example with a so "strange" reference system (lat/long, different x/y resolution) ?". I was trying to provide a workable example, so i copied most of my script, but tried to come up with a random dataset. Of course, the real data shows a "nice" map of the seafloor between 200 and 5000 m depth. Or is this not what you mean ? – Francesc Montserrat Mar 10 '17 at 18:21
  • Just that your example generated a raster with variable resolution in lat and lon (0.01088819 and 0.006359914), which is "unusual" for a map. This `dpt <- raster( xmx=-31.72083, xmn=-38.50417, ymn=-33.8875, ymx=-28.70417, res = c(0.01,0.01))` for example would give you an equal 0.01 resolution. – lbusett Mar 10 '17 at 21:03
0

So a couple of sanity checks first...

What is the actual area we're dealing with here? We'll base that on your stack and we'll use res() to pull the resolutions. It would be easy enough to grab them manually, but I prefer to call them.

 length(stk)*res(stk)[1]*res(stk)[2]

 70.32058

So I'm getting ~70.3 as the actual area of the stack. I'm only getting this from what you provided for data, so if the CRS or projections are wonky, thats on you. If we do the work to find the area for our subset and find it to be wildly different than this, we went astray.

Now I'm going to start at your 4th block of code where you did this:

 a <- stk[stk$layer.1 <= -1000 & stk$layer.1 >= -4000 & stk$layer.2 >= 10]

So now we have a layer, a where we've subsetted stk such that it is now a list of values which meet the criteria. We don't need that list, we really just want to know how many elements it contains.

Replace with:

 a<-length(stk[stk$layer.1 <= -1000 & stk$layer.1 >= -4000 & stk$layer.2 >= 10])

since we only want to know how many elements are in the list...

now, using res() to grab the x and y resolutions:

 a*res(stk)[1]*res(stk)[2]

Results in:

 29.80777

Based on the area of the raster we calculated previously, this seems reasonable, ~42% of the total area. This seems reasonable to me.

Regarding the issues with projections and coordinate systems in the other answers comments, YES that's a problem. But its on you to make sure your projections aren't wonky and make sense for what you are trying to do. Also remember that simply reassigning a projection system is not the same as re-projection. Probably one of the most common mistakes I see working with rasters in R is simply reassigning a CRS rather than reprojecting a raster. Its on you to know what form your data is coming in as and what you need to do with it to do the kind of math you want to do on it.

SeldomSeenSlim
  • 811
  • 9
  • 24
  • Maybe I'm missing something, but this `length(stk)*res(stk)[1]*res(stk)[2] = 70.32058` would be correct if we were dealing with a metric projection, so that the result of "res" is in meters (or multiples). Not with a lat/lon, where the "width" of a cell (in meters) varies with latitude. Also, what would be the measure units of the area resulting from that calc. ? decimal degrees squared ? – lbusett Mar 08 '17 at 15:50
  • Yes, you are correct. But that issue doesn't go away with any way of calculating area from a raster. It's on the user of the data to know that he's working in appropriate projection where doing any kind of area based math on a raster is sensible. I mentioned that in my opening and my closing. IF the user wants to get sensible results from calculating area on a raster, they need to make sure they've got their data appropriately projected for that work. The question here is about calculating area from a raster, not projections. – SeldomSeenSlim Mar 08 '17 at 16:31
  • I rather disagree. If i follow you correctly, from this statement it would follow that NO area computation/analysis can be done starting from a raster in geographic projection, which is IMO not true (see my comment below). – lbusett Mar 08 '17 at 21:27
  • I don't think you are following LoBu. My point is and remains, one can calculate area from a raster regardless of what coordinate system or projection it has, or even if it has one at all. Its on the user to be aware of what coordinate/projection system they are in, and if extrapolating to "real world" (m, ft, furlongs, fortnights, etc) units makes sense. I think the issue you are bringing up is specific to the contrived example here, because he made a mistake in how he made set his coordinate system/ projection. – SeldomSeenSlim Mar 08 '17 at 21:50
  • Well, the example isn't so much "contrieved": there are plenty of datasets in lat/lon projection used for various analyses. And, unless one reprojects a "metric" map into lat/lon beforehand for whatever reason, he is not doing anything wrong in wanting to compute an area, IMO. (Also, it's not really true that you don't need to know the reference system to compute an area. If working in geographic coordinates, imagine if you want to compute an area on Mars ? Even changing the reference datum on earth would give you (slightly, I admit) different results.) – lbusett Mar 08 '17 at 23:26
  • To calculate area from a raster: determine the x and y resolution; multiply these value by the number of pixels. ***That is it.*** Period. CRS had **nothing** to do with this. There is nothing intrinsically geospatial about a raster. That you can have a raster with a projection changes nothing about how you do basic math within a raster. If his data is incorrectly projected and the units are nonsensical, then that is an entirely separate issue. I think I've said this 6 separate times in this question. Here, I dont care at all what his units are. That's his problem. – SeldomSeenSlim Mar 08 '17 at 23:56
  • You may have said this 6 times, but I still don't agree. So, let's agree we disagree and that's it ;-). – lbusett Mar 09 '17 at 00:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137601/discussion-between-aron-boettcher-and-lobu). – SeldomSeenSlim Mar 09 '17 at 00:04
  • No. This is a question with a very specific, clear, and simple answer that any one who does basic work with raster should be able to answer and is relevant to this question. To leave pretending its unanswered does a disservice to anyone who has this same problem. – SeldomSeenSlim Mar 09 '17 at 00:09