TITLE: How much miombo is in each country DATE: 2018-11-15 AUTHOR: John L. Godlee ==================================================================== For some quick and dirty statistics to quote in the introduction to a report, I wanted to know how much of the miombo biome (as defined by White's vegetation map) was in Angola. Afterwards, I decided to try and apply the same methods to all the countries in southern Africa. I used R to do the analyses. First, load some packages: library(rgdal) library(rgeos) Next, import data on White's veg map and African countries. [White's veg map]: http://omap.africanmarineatlas.org/BIOSPHERE/pages/3_terrestrial%20v egetation.htm [African countries]: http://maplibrary.org/library/stacks/Africa/index.htm white_veg <- readOGR(dsn = "whitesveg", layer = "Whites vegetation") countries <- readOGR(dsn="africa", layer="Africa") The Coordinate reference system (CRS) isn't explicitly defined in either of the spatial objects, but it's a good guess that they will be WGS84 long-lat, so let's add that. proj4string(white_veg) <- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84") proj4string(countries) <- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84") I'm not interested in the other biomes defined in White's veg. map, only those that make up the "miombo". I use the miombo definition used in Ryan et al. (2016). [Ryan et al. (2016)]: http://rstb.royalsocietypublishing.org/content/royptb/371/1703/20150 312.full.pdf miombo <- white_veg[which( (white_veg$DESCRIPTIO == "Moist-infertile savanna") | (white_veg$DESCRIPTIO == "Mosaics of forest") | (white_veg$DESCRIPTIO == "Mopane savanna") | (white_veg$DESCRIPTIO == "Montane Forest") | (white_veg$DESCRIPTIO == "Hydropmorphic grassland") | (white_veg$DESCRIPTIO == "Arid-fertile savanna") | (white_veg$DESCRIPTIO == "Sedge and reed swamp")),] Then, I only want to keep countries that contain miombo, which I can do using gIntersects() from rgeos. country_list <- split(countries, countries$COUNTRY) intersections <- lapply(country_list, function(x) as.vector(unlist(gIntersects(x, miombo)))) country_list_miombo <- country_list[intersections == TRUE] Then, I define a function to find UTM zones, because countries vary in their UTM zone, so I can't just use a flat value. Converting from lat-long to UTM is necessary so I can get km^2 area estimates, rather than square degrees. The function takes any spatial object and uses the bounding box to estimate the UTM zone. # Define a function to find the UTM zone utm.zone <- function(x){ num <- floor(((mean(x@bbox[1,]) + 180)) / 6) + 1 let <- ifelse(mean(x@bbox[2,]) > 0, "N", "S") return(paste0(num, let)) } Then, time for a big lengthy, possibly overly messy function to return a list of miombo area stats for each country. miombo.country.perc <- function(country, miombo){ country_fix <- gBuffer( country, byid = TRUE, width = 0) miombo_country <- gIntersection( country_fix, miombo, byid = TRUE, drop_lower_td = TRUE) miombo_utm <- spTransform( miombo, CRS(paste0("+proj=utm +zone=", utm.zone(miombo), " ellps=WGS84"))) miombo_country_utm <- spTransform( miombo_country, CRS(paste0("+proj=utm +zone=", utm.zone(miombo_country), " ellps=WGS84"))) country_utm <- spTransform( country_fix, CRS(paste0("+proj=utm +zone=", utm.zone(country_fix), " ellps=WGS84"))) area_miombo_km2 <- gArea(miombo_utm) / 1e6 area_miombo_country_km2 <- gArea(miombo_country_utm) / 1e6 area_country_km2 <- gArea(country_utm) / 1e6 perc_miombo_country <- area_miombo_country_km2 / area_country_km2 * 100 perc_miombo_all <- area_miombo_country_km2 / area_miombo_km2 * 100 return(data.frame(area_miombo_country_km2, area_country_km2, perc_miombo_country, perc_miombo_all)) } Then I need to run the function on each country in the list of countries, using lapply(). country_miombo_stats <- lapply(country_list_miombo, miombo.country.perc, miombo = miombo) And finally there is a bit of cleaning up to get a tidy data frame. # Collapse the resulting list country_miombo_stats <- do.call("rbind", country_miombo_stats) # Add country as column country_miombo_stats$country <- rownames(country_miombo_stats) I could probably spend more time to just have the function and lapply call give me the tidy dataframe straight off, but I don't have the inclination. I think the most useful thing to come out of this little exercise is actually the UTM zone function, I think it's pretty neat.