I have 3 different rasters in a stack. I need to plot a panel plot and add different shapefiles on each panel. So far I managed to do the following;
## read the libraries
library(raster)
library(rgdal)
library(sp)
library(rworldmap)
library(OceanView)
##random raster object
r <- raster(ncol=40, nrow=20)
r[] <- rnorm(n=ncell(r))
# Create a RasterStack object with 3 layers
s <- stack(x=c(r, r*2, r**2))
##coordinate system
wgs<-CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
##reading the additional shape files
w <- spTransform(getMap(), wgs)
poly <- list(list("sp.lines", as(w, 'SpatialLines'), lwd =
0.5,col="black"))
##plotting with spplot
plot(spplot(s,layout=c(3,1),sp.layout=poly,
colorkey =list(space = "right"),
names.attr = c("a","b","c")))
so far I plotted the 3 rasters with a shapefile overlayed on it. Now I need to plot the 3 different contours one each on the panel plot. And also need to plot the windspeed arrows on each of the plot. I know to do this I need to use contour() and quiver() functions. However, I am unable to plot these.
## different raster stack for the contour plot
s1 <- stack(x=c(r/2, r*10, r**5))
##differnt wind components
lat= matrix(rep(seq(-90,90,length.out=20),each=20), ncol=20, byrow=TRUE)
lon=matrix(rep(seq(-180,180,length.out=20),each=20), ncol=20, byrow=F)
u=matrix(rep(sample(seq(-2,2,length.out=1000),20),each=20), ncol=20, byrow=TRUE)
v=matrix(rep(sample(seq(-2,2,length.out=1000),20),each=20), ncol=20, byrow=TRUE)
##plot the arrows
quiver2D(u = u,v=v,x = lon, y = lat,add=T,type="simple")
can any one help me with this? Any help would be appreciated.
First you need to figure out which function in the lattice paradigm is being called. That requires either looking at the ?spplot` help page (which I stupidly failed to do first), or following the classes and functions along the calling tree (which is what I actually did). And then look at the help page of the final function to see if there is a parameter you can pass to add contour lines:
showMethods("spplot",class="Raster", includeDefs=TRUE) # last argument need to see code
Function: spplot (package sp)
obj="Raster"
function (obj, ...)
{
.local <- function (obj, ..., maxpixels = 50000, as.table = TRUE,
zlim)
{
obj <- sampleRegular(obj, maxpixels, asRaster = TRUE,
useGDAL = TRUE)
if (!missing(zlim)) {
if (length(zlim) != 2) {
warning("zlim should be a vector of two elements")
}
if (length(zlim) >= 2) {
obj[obj < zlim[1] | obj > zlim[2]] <- NA
}
}
obj <- as(obj, "SpatialGridDataFrame")
spplot(obj, ..., as.table = as.table)
}
.local(obj, ...)
}
> showMethods("spplot",class="SpatialGridDataFrame",includeDefs=TRUE)
Function: spplot (package sp)
obj="SpatialGridDataFrame"
function (obj, ...)
spplot.grid(as(obj, "SpatialPixelsDataFrame"), ...)
> showMethods("spplot.grid",class="SpatialPixelsDataFrame",includeDefs=TRUE)
Function "spplot.grid":
<not an S4 generic function>
> spplot.grid
Error: object 'spplot.grid' not found
> getAnywhere(spplot.grid)
A single object matching ‘spplot.grid’ was found
It was found in the following places
namespace:sp
with value
function (obj, zcol = names(obj), ..., names.attr, scales = list(draw = FALSE),
xlab = NULL, ylab = NULL, aspect = mapasp(obj, xlim, ylim),
panel = panel.gridplot, sp.layout = NULL, formula, xlim = bbox(obj)[1,
], ylim = bbox(obj)[2, ], checkEmptyRC = TRUE, col.regions = get_col_regions())
{
if (is.null(zcol))
stop("no names method for object")
if (checkEmptyRC)
sdf = addNAemptyRowsCols(obj)
else sdf = as(obj, "SpatialPointsDataFrame")
if (missing(formula))
formula = getFormulaLevelplot(sdf, zcol)
if (length(zcol) > 1) {
sdf = spmap.to.lev(sdf, zcol = zcol, names.attr = names.attr)
zcol2 = "z"
}
else zcol2 = zcol
if (exists("panel.levelplot.raster")) {
opan <- lattice.options("panel.levelplot")[[1]]
lattice.options(panel.levelplot = "panel.levelplot.raster")
}
scales = longlat.scales(obj, scales, xlim, ylim)
args = append(list(formula, data = as(sdf, "data.frame"),
aspect = aspect, panel = panel, xlab = xlab, ylab = ylab,
scales = scales, sp.layout = sp.layout, xlim = xlim,
ylim = ylim, col.regions = col.regions), list(...))
if (all(unlist(lapply(obj#data[zcol], is.factor)))) {
args$data[[zcol2]] = as.numeric(args$data[[zcol2]])
if (is.null(args$colorkey) || (is.logical(args$colorkey) &&
args$colorkey) || (is.list(args$colorkey) && is.null(args$colorkey$at) &&
is.null(args$colorkey$labels))) {
if (!is.list(args$colorkey))
args$colorkey = list()
ck = args$colorkey
args$colorkey = NULL
args = append(args, colorkey.factor(obj[[zcol[1]]],
ck))
}
else args = append(args, colorkey.factor(obj[[zcol[1]]],
ck, FALSE))
}
ret = do.call(levelplot, args)
if (exists("panel.levelplot.raster"))
lattice.options(panel.levelplot = opan)
ret
}
<bytecode: 0x7fae5e6b7878>
<environment: namespace:sp>
You can see that it supports additional arguments passed via , list(...)). As it happens it's therefore fairly easy to add contour lines to a levelplot with contour=TRUE although that only appears in the Arguments list, but not in the named arguments in the Usage section for levelplot. Nonetheless testing in the example on ?levelplot page shows that it succeeds. Your example is not a particularly good one to illustrate with, since it is so fine-grained and has no pattern of ascending or descending levels. Nonetheless adding contour=TRUE, to the arguments to spplot does produce black contour lines. (The timestamp is due to lattice code in my Rprofile setup so won't show up on your device).
png(); plot(spplot(s,layout=c(3,1),sp.layout=poly, contour=TRUE,
colorkey =list(space = "right"),
names.attr = c("a","b","c"))) ; dev.off()
If one gets around to hacking spplot.grid or perhaps sp::panel.gridplot, then this material from the author of levelplot might be of use:
https://markmail.org/search/?q=list%3Aorg.r-project.r-help+lattice+add+contours+to+levelplot#query:list%3Aorg.r-project.r-help%20lattice%20add%20contours%20to%20levelplot%20from%3A%22Deepayan%20Sarkar%22+page:1+mid:w7q4l7dh6op2lfmt+state:results
Related
I have a RC-class with a plot method in r.
I have written code to run ggplot() twice, but it will only plot the latest one called. Any suggestions on why this is happening/how to fix it?
Below is an example of my structure.
testClass <- setRefClass("testClass",
fields = list(
x = "numeric",
y = "numeric"
),
methods = list(
initialize = function(x, y) {
.self$x = x
.self$y = y
},
plot = function() {
ggplot2::ggplot(...)
ggplot2::ggplot(...)
}))
I have generated several Utilisation Distributions (UD) with AdehabitatHR and stored them as Geotiffs. I am now using the same UDs with the Lattice package to generate some maps and saving them to a high-res tiff image with LZW compression. Problem is that I have literally hundreds of maps to make, save and name. Is there a way automatically do this once i have loaded all the necessary files from a directory? Each one of my UDs has the following structure of the filename "UD_resolution_species_area_year_season. tif" and in the final name I give to my map I would like to keep the same structure (or entire filename) but add the prefix "blablabla_" e.g. "blablabla_UD_resolution_species_area_year_season.tiff". The image also include a main name, a capital letter, which should also change.
At the moment I am using the following:
rlist = list.files(getwd(), pattern = "tif$", full.names = FALSE)
for (i in rlist) {
assign(unlist(strsplit(i, "[.]"))[1], raster(i))
}
shplist = list.files(getwd(), pattern = "shp$", full.names = FALSE)
for (i in shplist) {
assign(unlist(strsplit(i, "[.]"))[1], readOGR(i))
}
UD <- 'UD_resolution_species_area_year_season'
ext <- extent(UD) + 0.3 # set the extent for the plot
aa <-
quantile(UD,
probs = c(0.25, 0.75),
type = 8,
names = TRUE)
my.at <- c(aa[1], aa[2])
my.at <- round(my.at, 3)
maxval <- maxValue(UD)
tiff(
"C:/myworkingdirectory/maps/blablabla_UD_resolution_species_area_year_season.tiff",
res = 600,
compression = "lzw",
width = 15,
height = 15,
units = "cm"
)
levelplot(
UD,
xlab = "",
ylab = "",
xlim = c(ext[1], ext[2]),
ylim = c(ext[3], ext[4]),
margin = FALSE,
contour = FALSE,
col.regions = viridis(1000),
colorkey = list(at = seq(0, maxval)),
main = "A",
maxpixels = 2e5
) + latticeExtra::layer(sp.polygons(Land, fill = "grey50", col = NA)) + contourplot(
`UD`,
at = my.at[1],
labels = FALSE,
margin = FALSE,
lty = 2,
col = "orange",
pretty = TRUE
) + contourplot(
UD,
at = my.at[2],
labels = FALSE,
margin = FALSE,
lty = 2,
col = "red",
pretty = TRUE,
)
dev.off()
It is a common beginners mistake to use assign. Do not use it, it creates the type of trouble you are now facing. In stead, you can make lists and/or use a loop.
Also what you are asking is basic R stuff, but you are complicating the question with adding lots of irrelevant detail about setting the extent, and levelplot. It is better to learn about doing these basic things by removing the clutter and focus on a simple case first. That is also how you should write questions for this forum.
In essence you have a bunch of files you want to process. Below I show how you can loop over a vector of the names and then loop and do what you need to do in that loop.
library(raster)
rastfiles <- list.files(pattern = "tif$", full.names=TRUE)
outputfiles <- file.path("output/path", paste0("prefix_", basename(rastfiles)))
for (i in 1:length(rastfiles))
r <- raster(rastfiles[i])
png(outputfiles[i])
plot(r)
dev.off()
}
You can also first read all the files into a list
rastfiles <- list.files(pattern = "tif$", full.names=TRUE)
rlist <- lapply(rastfiles, raster)
names(rlist) <- gsub(".tif$", "", basename(rastfiles))
rastfiles <- list.files(pattern = "shp$", full.names=TRUE)
slist <- lapply(shpfiles, readOGR)
names(slist) <- gsub(".shp$", "", basename(shpfiles))
And perhaps create a vector of output filenames
outputtif <- file.path("output/dir", basename(rastfiles))
And then loop over the items in the list, or the output filenames
I have vertices and indices data for human face here. I have a post one year ago on plotting 3D facial surface mesh based on these data. Now, I want to plot only the right half and mid-facial vertices while ignoring the left side vertices. Based on my earlier plot, I tried the following code:
library(tidyverse)
library(readxl)
library(rgl)
vb <- read_excel("...\\vb.xlsx", sheet = "Sheet1", col_names = F)
it <- read_excel("...\\it.xlsx", sheet = "Sheet1", col_names = F)
# Extract vertices for the right side
lm_right_ind <- which(vb[,1] < 0)
vb_mat_right <- t(vb[lm_right_ind, ])
vb_mat_right <- rbind(vb_mat_right, 1)
rownames(vb_mat_right) <- c("xpts", "ypts", "zpts", "")
vertices1_right <- c(vb_mat_right)
# Extract `it` whose rows do not contain vertices on the left side
# Left-side vertices have vb[,1] greater than 0
lm_left_ind <- which(vb[,1] > 0)
leftContain <- NULL
for (i in 1: dim(it)[1]) {
if (T %in% (it[i,] %in% lm_left_ind)) {
leftContain[i] <- i
} else {leftContain[i] <- NA}
}
leftContain <- leftContain[!is.na(leftContain)]
# Remove indices that involve left-side vertices
it_rightMid <- it[-leftContain,]
it_mat_right <- t(as.matrix(it_rightMid))
rownames(it_mat_right) <- NULL
indices_right <- c(it_mat_right)
# Plot
try1_right <- tmesh3d(vertices = vertices1_right, indices = indices_right, homogeneous = TRUE,
material = NULL, normals = NULL, texcoords = NULL)
# Use addNormals to smooth the plot. See my Stackoverflow question:
# https://stackoverflow.com/questions/53918849/smooth-3d-trangular-mesh-in-r
try12_right <- addNormals(try1_right)
shade3d(try12_right, col="#add9ec", specular = "#202020", alpha = 0.8)
I got an error whing trying to obtain try12_right:
Error in v[, it[3, i]] : subscript out of bounds.
I did exactly as what I did in my earlier plot but why something went wrong here? Thank you.
Here's an example of using a clipping plane to leave off the left hand side of a mesh object:
library(rgl)
open3d()
root <- currentSubscene3d()
newSubscene3d("inherit", "inherit", "inherit", parent = root) # Clipping limited to this subscene
shade3d(addNormals(subdivision3d(icosahedron3d(), 2)), col = "pink")
clipplanes3d(a = 1, b = 0, c = 0, d = 0)
useSubscene3d(root)
decorate3d()
The fiddling with subscenes limits the clipping to just the shaded sphere, not everything else in the picture.
This produces this output:
If there's nothing else there, it's simpler:
library(rgl)
open3d()
shade3d(addNormals(subdivision3d(icosahedron3d(), 2)), col = "pink")
clipplanes3d(a = 1, b = 0, c = 0, d = 0)
which produces
I have the following scripts:
library("gplots")
mydata <- mtcars
mydata.nr <- nrow(mydata)
mydata.newval <- data.frame(row.names=rownames(mydata),new.val=-log(runif(mydata.nr)))
# Functions
hclustfunc <- function(x) hclust(x, method="complete")
distfunc <- function(x) dist(x,method="euclidean")
# Set colors
hmcols <- rev(redgreen(256));
# Plot the scaled data
heatmap.2(as.matrix(mydata),dendrogram="row",scale="row",col=hmcols,trace="none", margin=c(8,9), hclust=hclustfunc,distfun=distfunc);
Which generate the following heatmap:
Now given a new data.frame which contain new values for each cars:
mydata.nr <- nrow(mydata)
mydata.newval <- data.frame(row.names=rownames(mydata),new.val=-log(runif(mydata.nr)))
I want to create a single column heatmap with gradient gray positioned next to row names.
How can I achieve that in R heatmap.2?
Does this do what you want? You can use the RowSideColors option to add a column to the side of the heatmap.
new.vals = mydata.newval[,1]
mydata.newval$scaled = ( new.vals - min(new.vals) ) /
( max(new.vals) - min(new.vals) )
mydata.newval$gray = gray( mydata.newval$scaled )
heatmap.2( as.matrix(mydata),
dendrogram = "row", scale = "row",
col = hmcols, trace = "none",
margin = c(8,9),
hclust = hclustfunc, distfun = distfunc,
RowSideColors=mydata.newval$gray )
If you want the gray column in between the heatmap and the labels, there isn't a simple
way to do that with heatmap.2; I don't think it was designed for
such purposes. One way to hack it together would be to make the gray values
go from 10 to 11 (or something out of the range of the rest of the data). Then
you would change the colors mapped to the breaks (see here). However, this
would make your key look pretty funky.
# heatmap.2 does the clustering BEFORE the scaling.
# Clustering after scaling might give different results
# heatmap.2 also reorders the dendrogram according to rowMeans.
# (Code copied directly from the heatmap.2 function)
x = as.matrix(mydata)
Rowv = rowMeans(x, na.rm = TRUE)
hcr = hclustfunc(distfunc(x))
ddr = as.dendrogram(hcr)
ddr = reorder(ddr, Rowv) # the row dendrogram
# Scale the data as heatmap.2 does
rm = rowMeans(x, na.rm = TRUE)
x = sweep(x, 1, rm)
sx = apply(x, 1, sd, na.rm = TRUE)
x = sweep(x, 1, sx, "/")
# add the new data as a column
new.vals = mydata.newval[,1]
new.vals.scaled = ( new.vals - min(new.vals) ) /
( max(new.vals) - min(new.vals) ) # scaled from 0 to 1
x = cbind( x, gray = max(x) + new.vals.scaled + 0.1 )
# make the custom breaks and colors
edge = max(abs(x-1.1))
breaks = seq(-edge,edge+1.1,length.out=1000)
gradient1 = greenred( sum( breaks[-length(breaks)] <= edge ) )
gradient2 = colorpanel( sum( breaks[-length(breaks)] > edge ), "white", "black" )
hm.colors = c(gradient1,gradient2)
hm = heatmap.2( x, col=hm.colors, breaks=breaks,
scale="none",
dendrogram="row", Rowv=ddr,
trace="none", margins=c(8,9) )
Although this hack works, I would look for a more robust solution using more flexible packages that play with different viewports using the grid package.
I need to remove the box frame around the figure in R package "raster", but I cannot figure out which argument I should change. The example is as follows:
library(raster)
r <- raster(nrows=10, ncols=10)
r <- setValues(r, 1:ncell(r))
plot(r)
plot(r,axes=F)
This works:
plot(r, axes=FALSE, box=FALSE)
To learn how you could have found that out for yourself, have a look at the underlying functions by trying the following. (The calls to showMethods() and getMethod() are needed because the raster package makes extensive use of S4 methods rather than the more commonly used S3 methods.)
showMethods("plot")
getMethod("plot", c("Raster", "ANY"))
getAnywhere(".plotraster2")
getAnywhere(".rasterImagePlot")
args(raster:::.rasterImagePlot)
# function (x, col, add = FALSE, legend = TRUE, horizontal = FALSE,
# legend.shrink = 0.5, legend.width = 0.6, legend.mar = ifelse(horizontal,
# 3.1, 5.1), legend.lab = NULL, graphics.reset = FALSE,
# bigplot = NULL, smallplot = NULL, legend.only = FALSE, lab.breaks = NULL,
# axis.args = NULL, legend.args = NULL, interpolate = FALSE,
# box = TRUE, breaks = NULL, zlim = NULL, zlimcol = NULL, fun = NULL,
# asp, colNA = NA, ...)
The best I can suggest is
plot(r,axes=F,useRaster=F)
The option bty='n' usually gets rid of the box, but the raster-plotting function seems to be drawing its own box on top of the regular box that you can't get rid of.