Trying to create a thematic map to represent data on a LargeSpatialPolygonDataFrame and I'm having difficulty creating a forced scale.
I'd like to make the following scale: seq(0,4500,500) to create ten different fill categories regardless of if the data frame has data in that range or not like the following image.
Texas_LMA SpatialPolygonDataFrame:
> Texas_LMA
class : SpatialPolygonsDataFrame
features : 33
extent : -106.6278, -93.52764, 25.85646, 36.5004 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0
variables : 10
names : LMA, Sol_index, Capacity, LMA.data, Technology, Water_Capacity_Value, Robust., X, Water_Capacity, Water_Capacity_String
min values : 1, 135, 21, 1, Biomass, 0.00, 1, NA, 0, 0%
max values : 33, 135, 1739, 32, Biomass | Wind, 0.84, 1, NA, 84, 84%
Has the following Capacity ranges
> unique(Texas_LMA$Capacity)
[1] 892 1739 156 NA 21 495
I'm using tmap to create the thematic map with the following code:
Fixed_Capacity_Heatmap <- tm_shape(Texas_LMA)+
tm_fill("Capacity",style="fixed",breaks=seq(0,4500,500))+
tm_borders()
Results of the plot when there aren't enough categories Capacity Plot with 5 categories
This issue should have been fixed as of https://github.com/mtennekes/tmap/commit/3a33563a4336042307320f470dc8189fb0572477, i.e. CRAN version 1.4-1. The problem was that classInt was struggling with numeric variables with only a few unique values. Please let me know if it still doesn't work, preferably with a reproducible example.
Related
My goal is to perform a random-forest classification for multispectral, high-resolution UAV imagery.
Currently I am preparing training data for the algorithm. I started writing a for-loop to extract the pixel values of my raster (RasterBrick) using the training area polygons (SpatialPolygonsDataFrame). However, a colleague pointed me to Rs extract function which is probably easier and looks far less cluttered.
###Rast1B is a RasterBrick and contains pixel value info on 4 Bands (B1-B4)
> print(Rast1B)
class : RasterBrick
dimensions : 10000, 10000, 1e+08, 4 (nrow, ncol, ncell, nlayers)
resolution : 0.1, 0.1 (x, y)
extent : 361000, 362000, 5619000, 5620000 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
source : C:/Users/foofoo
names : B1, B2, B3, B4
min values : 0, 0, 0, 0
max values : 255, 255, 255, 255
###Shape1B is a SpatialPolygonDataframes and contains ROI polygons as well as a class id column (1 or 2, there are only two classes)
> print(Shape1B)
class : SpatialPolygonsDataFrame
features : 104
extent : 361420.1, 361607.7, 5619007, 5619334 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs
variables : 1
names : id
min values : 1
max values : 2
Extraction returns a DF with 5 columns. The class id column from the SpatialPolygonsDF is lost and replaced by the polygon ids, though.
trainingDF <- extract(Rast1B, Shape1B, df=TRUE)
###Just the first three rows to give an overview
> print(bar2)
ID B1 B2 B3 B4
1 1 105 123 145 116
2 1 112 131 154 123
3 1 116 135 153 126
I'm looking for a way to use extract while maintaining the class id for each pixel as this is required for the creation of training and testing data. Its probably also possible to add the column manually after extraction based on the polygon ids but I'm unsure how to approach this since I'm very new to R.
Any input is appreciated!
ID refers to the (order of the) polygons; so you can use an attribute of those polygons, in your case the field id, to make the connection. The following should do it
trainingDF <- data.frame(trainingDF)
trainingDF$id <- Shape1B$id[trainingDF$ID]
I downloaded worlclim/BIO climatic data which has 16 layers. 1-11 layers of which are temperature data. Rests are precipitation data. When I checked document, I should convert unit of temperature data by different conversion factors. 1-2,4-11 layers should be divided by 10 to convert degree celcius and 3-4 layers by 100. To do this, I wrote following code:
temp1<-clim[[1:2]]/10
temp2 <-clim[[5:11]]/10
temp3<-clim[[3:4]]/100
Stack them back according to the same order as they were in original data:
clim <-stack(temp1,temp3,temp2)
My question is how to calculate different formula on different layer and stack them back to original order? I want to know another way to do these steps.
Thank you!
Easist way could be to define a vector of "dividing factors" and then simply divide the stack by that vector. In this way, you do not need to put the bands in the "original" order:
library(raster)
a <- raster::raster(ncols = 10, nrows = 10)
a <- raster::init(a, runif)
# create a stack
b <- raster::stack(a,a,a,a,a,a)
# define vector of dividing factors
divs <- c(1,1,10,10,100,100)
# compute
c <- b / divs
c
class : RasterBrick
dimensions : 10, 10, 100, 6 (nrow, ncol, ncell, nlayers)
resolution : 36, 18 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : layer.1, layer.2, layer.3, layer.4, layer.5, layer.6
min values : 5.919103e-03, 5.919103e-03, 5.919103e-04, 5.919103e-04, 5.919103e-05, 5.919103e-05
max values : 0.99532098, 0.99532098, 0.09953210, 0.09953210, 0.00995321, 0.00995321
I want to crop an elevation raster to add it to a raster stack. It's easy, I did this before smoothly, adding a ecoregions raster to the same stack. But with the elevation one, just doesn't work. Now, there are several questions here in overflow adressing this issue and I tryed a lot of things...
First of all, we need this:
library(rgdal)
library(raster)
My stack is predictors2:
#Downloading the stack
predictors2_full<-getData('worldclim', var='bio', res=10)
#Cropping it, I don' need the whole world
xmin=-120; xmax=-35; ymin=-60; ymax=35
limits <- c(xmin, xmax, ymin, ymax)
predictors2 <- crop(predictors2_full,limits)
Then I've downloaded the terr_ecorregions shapefile here: http://maps.tnc.org/files/shp/terr-ecoregions-TNC.zip
setwd("~/ORCHIDACEAE/Ecologicos/w2/layers/terr-ecoregions-TNC")
ecoreg = readOGR("tnc_terr_ecoregions.shp") # I've loaded...
ecoreg2 <- crop(ecoreg,extent(predictors2)) # cropped...
ecoreg2 <- rasterize(ecoreg2, predictors2) # made the shapefile a raster
predictors4<-addLayer(predictors2,elevation,ecoreg2) # and added the raster
# to my stack
With elevation, I just can't. The Digital elevation model is based in GMTED2010, which can be downloaded here: http://edcintl.cr.usgs.gov/downloads/sciweb1/shared/topo/downloads/GMTED/Grid_ZipFiles/mn30_grd.zip
elevation<-raster("w001001.adf") #I've loaded
elevation<-crop(elevation,predictors2) # and cropped
But elevation gets a slightly different extent instead of predictors2's extent:
> extent(elevation)
class : Extent
xmin : -120.0001
xmax : -35.00014
ymin : -60.00014
ymax : 34.99986
>
I tried to make then equal by all means I read about in questions here...
I tried to extend so elevation's ymax would meet predictors2's ymax
elevation<-extend(elevation,predictors2) #didn't work, extent remains the same
I tried the opposite... making predictors2 extent meet elevation's extent... nothing either.
But then I read that
You might not want to play with setExtent() or extent() <- extent(), as you could end with wrong geographic coordinates of your rasters - #ztl, Jun 29 '15
And I tried to get the minimal common extent of my rasters, following #zlt answer in another extent question, by doing this
# Summing your rasters will only work where they are not NA
r123 = r1+r2+r3 # r123 has the minimal common extent
r1 = crop(r1, r123) # crop to that minimal extent
r2 = crop(r2, r123)
r3 = crop(r3, r123)
For that, first I had to set the resolutions:
res(elevation)<-res(predictors2) #fixing the resolutions... This one worked.
But then, r123 = r1+r2+r didn't work:
> r123=elevation+ecoreg2+predictors2
Error in elevation + ecoreg2 : first Raster object has no values
Can anyone give me a hint on this? I really would like to add my elevation to the raster. Funny thing is, I have another stack named predictors1 with the exact same elevation's extent... And I was able to crop ecoreg and add ecoreg to both predictors1 and predictors2... Why can't I just do the same to elevation?
I'm quite new to this world and runned out of ideas... I appreciate any tips.
EDIT: Solution, Thanks to #Val
I got to this:
#Getting the factor to aggregate (rasters are multiples of each other)
res(ecoreg2)/res(elevation)
[1] 20 20 #The factor is 20
elevation2<-aggregate(elevation, fact=20)
elevation2 <- crop(elevation2,extent(predictors2))
#Finally adding the layer:
predictors2_eco<-addLayer(predictors2,elevation2,ecoreg)
New problem, thought...
I can't write stack to a geotiff
writeRaster(predictors2_eco, filename="cropped_predictors2_eco.tif", options="INTERLEAVE=BAND", overwrite=TRUE)
Error in .checkLevels(levs[[j]], value[[j]]) :
new raster attributes (factor values) should be in a data.frame (inside a list)
I think you're having issues because you're working with rasters of different spatial resolutions. So when you crop both rasters to the same extent, they'll have a slightly different actual extent because of that.
So if you want to stack rasters, you need to get them into the same resolution. Either you disaggregate the raster with the coarser resolution (i.e. increase the resolution by resampling or other methods) or you aggregate the raster with the higher resolution (i.e. decrease the resolution with for instance taking the mean over n pixel).
Please note that if you change the extent or resolution with setExtent(x), extent(x) <-, res(x) <- or similar will NOT work, since you're just changing slots in the raster object, not the actual underlying data.
So to bring the rasters into a common resolution, you need to change the data. You can use the functions (amongst others) aggregate, disaggregate and resample for that purpose. But since you're changing data, you need to be clear on what you're and the function you use is doing.
The most handy way for you should be resample, where you can resample a raster to another raster so they match in extent and resolution. This will be done using a defined method. Per default it's using nearest neighbor for computing the new values. If you're working with continuous data such as elevation, you might want to opt for bilinear which is bilinear interpolation. In this case you're actually creating "new measurements", something to be aware of.
If your two resolutions are multiples of each other, you could look into aggregate and disaggregate. In the case of disaggregate you would split a rastercell by a factor to get a higher resolution (e.g. if your first resolution is 10 degrees and your desired resolution is 0.05 degrees, you could disaggregate with a factor of 200 giving you 200 cells of 0.05 degree for every 10 degree cell). This method would avoid interpolation.
Here's a little working example:
library(raster)
library(rgeos)
shp <- getData(country='AUT',level=0)
# get centroid for downloading eco and dem data
centroid <- coordinates(gCentroid(shp))
# download 10 degree tmin
ecovar <- getData('worldclim', var='tmin', res=10, lon=centroid[,1], lat=centroid[,2])
ecovar_crop <- crop(ecovar,shp)
# output
> ecovar_crop
class : RasterBrick
dimensions : 16, 46, 736, 12 (nrow, ncol, ncell, nlayers)
resolution : 0.1666667, 0.1666667 (x, y)
extent : 9.5, 17.16667, 46.33333, 49 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : tmin1, tmin2, tmin3, tmin4, tmin5, tmin6, tmin7, tmin8, tmin9, tmin10, tmin11, tmin12
min values : -126, -125, -102, -77, -33, -2, 19, 20, 5, -30, -74, -107
max values : -31, -21, 9, 51, 94, 131, 144, 137, 106, 60, 18, -17
# download SRTM elevation - 90m resolution at eqt
elev <- getData('SRTM',lon=centroid[,1], lat=centroid[,2])
elev_crop <- crop(elev, shp)
# output
> elev_crop
class : RasterLayer
dimensions : 3171, 6001, 19029171 (nrow, ncol, ncell)
resolution : 0.0008333333, 0.0008333333 (x, y)
extent : 9.999584, 15.00042, 46.37458, 49.01708 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : srtm_39_03
values : 198, 3865 (min, max)
# won't work because of different resolutions (stack is equal to addLayer)
ecoelev <- stack(ecovar_crop,elev_crop)
# resample
elev_crop_RS <- resample(elev_crop,ecovar_crop,method = 'bilinear')
# works now
ecoelev <- stack(ecovar_crop,elev_crop_RS)
# output
> ecoelev
class : RasterStack
dimensions : 16, 46, 736, 13 (nrow, ncol, ncell, nlayers)
resolution : 0.1666667, 0.1666667 (x, y)
extent : 9.5, 17.16667, 46.33333, 49 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
names : tmin1, tmin2, tmin3, tmin4, tmin5, tmin6, tmin7, tmin8, tmin9, tmin10, tmin11, tmin12, srtm_39_03
min values : -126.0000, -125.0000, -102.0000, -77.0000, -33.0000, -2.0000, 19.0000, 20.0000, 5.0000, -30.0000, -74.0000, -107.0000, 311.7438
max values : -31.000, -21.000, 9.000, 51.000, 94.000, 131.000, 144.000, 137.000, 106.000, 60.000, 18.000, -17.000, 3006.011
I ran some species distribution models (SDM), using maxent of the 'dismo' package for several species, so I saved each one as a raster file.
After that I transformed this environmental suitability raster (SDM result) into a binary map based on a determined threshold value.
I used this function and these command lines to do the conversion:
bin <- function(x) {
ifelse(x <= 0.7, 0,
ifelse(x > 0.7, 1, NA)) }
sp_bin <- calc(spp, fun=bin) # "spp" is the object within my raster
writeRaster(sp_bin,filename='Results/RasterBin/Patagioenas_fasciata_bin.tif')
Now I have a raster of that binary map (i.e., with only two values, 0 and 1).
class : RasterLayer
dimensions : 1117, 1576, 1760392 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -68.39167, -55.25833, -0.7333333, 8.575 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : C:\Users\Ramiro\Desktop\SDM\Results\RasterBin\Patagioenas_fasciata_bin.tif
names : Patagioenas_fasciata_bin
values : 0, 1 (min, max)
I tried to use the following argument to bring the sum of the pixels with value 1, assuming that the others would be 0 and therefore the total sum would equal the area of all pixels with values 1:
cellStats(spp, stat='sum', na.rm=TRUE)
And I got a numerical result (12,471) that I thought was correct. However, I did this for other species, and therefore other rasters, and the result remains the same, although each species has a distinct binary map.
How can I calculate the area only for pixels with value "1" in a binary map raster in R?
Thank you all for the attention.
I am trying extract the data of two overlapping set of rasters (one, a stack of 35 rasters, all from the same source and the second an elevation raster) to get a data.frame of the values (mean of the values) of each pixel of all the rasters.
The description of the raster stack is the following:
> stack_pacifico
class : RasterStack
dimensions : 997, 709, 706873, 35 (nrow, ncol, ncell, nlayers)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -81.62083, -75.7125, 0.3458336, 8.654167 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
names : F101992.v//ts.avg_vis, F101993.v//ts.avg_vis, F101994.v//ts.avg_vis, F121994.v//ts.avg_vis, F121995.v//ts.avg_vis, F121996.v//ts.avg_vis, F121997.v//ts.avg_vis, F121998.v//ts.avg_vis, F121999.v//ts.avg_vis, F141997.v//ts.avg_vis, F141998.v//ts.avg_vis, F141999.v//ts.avg_vis, F142000.v//ts.avg_vis, F142001.v//ts.avg_vis, F142002.v//ts.avg_vis, ...
min values : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
max values : 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, ...
And for the elevation raster:
> elevation_pacifico
class : RasterLayer
dimensions : 997, 709, 706873 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -81.62083, -75.7125, 0.3458336, 8.654167 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : COL_msk_alt
values : -16, 5164 (min, max)
It is my first time with raster data and I want to extract the data by grids of 1km2 (or less). I know the resolution of both rasters can be coerced to fit into that area requirement, also both dimensions are equal, so the number of pixels per raster is the same.
My question is, can I only merge all the rasters (the ones in the stack and the elevation raster) and extract the data with the confidence that all the pixels overlap (or are in the same place)? Or do I have to create a master SpatialGrid or SpatialPixel object and then extract the raster data to these objects?
Thanks in advance,
Data from the raster stack can be downloaded by clicking at this link (if you want to download all the stack, you can use the script in https://github.com/ivanhigueram/nightlights):
http://www.ngdc.noaa.gov/eog/data/web_data/v4composites/
Elevation:
#Download country map and filter by pacific states
colombia_departments <- getData("GADM", download=T, country="CO", level=1)
pacific_littoral <- c(11, 13, 21, 30)
pacific_littoral_map <- colombia_departments[colombia_departments#data$ID_1 %in% pacific_littoral, ]
#Download elevation data and filter it for pacific states
elevation <- getData("alt", co="COL")
elevation_pacifico <- crop(elevation, pacific_littoral_map)
elevation_pacifico <- setExtent(elevation_pacifico, rasters_extent)
If the resolutions, extents and coordinate systems of the two raster objects are identical, then the cells will overlap perfectly. You can confirm this by looking at the coordinates:
coordinates(stack_pacifico)
coordinates(elevation_pacifico)
# are they the same?
identical(coordinates(stack_pacifico), coordinates(elevation_pacifico))
You can extract all cell values for each object using one of the following:
as.data.frame(r)
values(r)
r[]
extract(r, seq_len(ncell(r)))
where r is your raster object.
(These do not all have consistent behaviour for single raster layers - as.data.frame(r) ensures the result is a data.frame, which would have a single column if r is a single raster layer; in contrast the alternatives would return a simple vector if used with a single raster layer.)
The rows of as.data.frame(stack_pacifico) correspond to cells at the same coordinates as do the rows of as.data.frame(elevation_pacifico) (or, equivalently, the elements ofvalues(elevation_pacifico)`).
Or do this:
s <- stack(elevation_pacifico, stack_pacifico)
d <- values(s)