I can't really set the boundaries of my plotted shapefile. I'm plotting the shapefile first to get nice x- and y- labels in degrees first, plotting the data afterwards. In the end, I'm plotting my shapefile over the data again. ylim is changeable, but xlim seems to be solely dependend on ylim changes, because I cant vary xlim itself. It only varies, when I change ylim without changing xlim, like as it is an aspect ratio issue.
I want to limit the x-axis between 8.5 and 11.5 degrees.
A link to the shapefile and raster in question: https://www.dropbox.com/sh/l42mty01mwtm8qc/AADqjNbGkmNwx3o9aFceGrkya?dl=0
My code:
library(rgeos)
library(rgdal)
library(raster)
Sys.setlocale(category = "LC_ALL", locale = "C") # In the case German Umlauts are a problem while reading the shapefile
map <- readOGR("C:\\Path\\NATRAUM_MR_utm32.shp")
# Exclude unnecessary regions
map <- subset(map, NATREGNR != 3)
map <- subset(map, NATREGNR != 4)
map <- subset(map, NATREGNR != 9)
map_wgs84 <- spTransform(map, crs(raster.percent.change.rcp85.1971_2005.2071_2100))
# Pixelplot
par(mar = c(3, 3, 2, 1)) # For saving pictures through a device like pdf
m <- plot(map_wgs84, axes=TRUE, xlim = c(8.5, 11.5), ylim=c(51.35, 53.15), cex = .5,
bty = "n")
# Colortable for legend
colTab <- c("#0033CC", "#3366FF", "#6699FF","#99FFFF", "#FFCC99","#FF9933", "#FF4D00","#660000")
N <- length(colTab)
breaks <- seq(-2, 2, length.out= N+1 )
plot(raster.percent.change.rcp85.1971_2005.2071_2100, col = colTab, breaks = breaks,
axis.args = list(cex.axis = 1, at = breaks, labels = breaks, mgp = c(1, 0, 0), tck = 0.1),
legend.args = list(text='Change [%]', side=4, font=2, line=1.75, cex=0.7),
add = TRUE
)
plot(map_wgs84, add = TRUE) # Plotting shapefile over data
You need to resize your plotting window. You can use par(fin=c(x,y)), or png() to set a ratio that works, and fix that in code.
This is because for maps, the correct aspect (ratio of vertical and horizontal extent) is enforced. For planar data the aspect is 1. For angular data (longitude/latitude) it varies by latitude.
With some data.
library(raster)
p <- shapefile(system.file("external/lux.shp", package="raster"))
Compare:
par(fin=c(6,6))
plot(p, axes=T, xlim=c(6,6.2), ylim=c(49.6, 49.8))
par(fin=c(4, 6))
plot(p, axes=T, xlim=c(6,6.2), ylim=c(49.6, 49.8))
I am using vegan library to make some plots, with this code:
raremax <- min(colSums(mydata))
col <- palette()
lty <- c("solid", "dashed", "longdash", "dotdash")
pars <- expand.grid(col = col, lty = lty, stringsAsFactors = FALSE)
out <- with(pars[1:18, ], rarecurve(mydata, step = 100, sample = raremax,
cex =0.6, ylab="OTUs", label=F, col=col, lty=lty, lwd=2))
Then I add a legend using this code:
legend("bottomright", names(mydata), col=pars[1:18,1], lty= pars[1:18,2],
lwd=2, cex=0.5, xjust=1, ncol=2, x.intersp=0.5, y.intersp=0.5, bg="white")
The resulting graph looks like this:
I would like to reduce the space between legend columns, also reducing the size of the legend box, but I can't find a way to do that.
Anyone could provide me some help?
A combination of the legend() parameters "x.intersp" and "text.width" should be helpful.
Decreasing "x.intersp" (default value = 1, for me 0.25 looked good) should move your the legend labels closer to their respective points. Decreasing "text.width" (default value=NULL, for me 0.045 looked good) moves the columns closer together.
For an assignment I'm working on, we are performing a distribution model using occurrence points. The plot that appears is showing an extent that is too large and I would like to change the xlim and ylim so that the plot is showing the area only with the occurrence points (orange area). I have tried manipulating it a few ways but it never focuses on the extent that I want. I guess what I really don't understand is what the xlim and ylim are specifying.
sparrow= gbif("ammodramus", "maritimus*", geo=FALSE)
sparrow <- subset(sparrow, !is.na(lon) & !is.na(lat))
library(maptools)
data(wrld_simpl)
plot(wrld_simpl, xlim=c(-80,70), ylim=c(-60,10), axes=TRUE, col="lightyellow")
points(sparrow$lon, sparrow$lat, col="orange", pch=20, cex=0.75)
points(acgeo$lon, acgeo$lat, col="red", cex=0.75)
When you do
library(sp)
coordinates(sparrow) = ~lon + lat
proj4string(sparrow) = proj4string(wrld_simpl)
plot(sparrow, col = 'red', cex = .75, axes = TRUE)
plot(wrld_simpl, add = TRUE)
the plot takes xlim and ylim from sparrow.
I want to make a legend on my graph, which is generated by plot() function. The original legend() function will generate a list which has only 1 column. How can I make a legend which has 2 columns?
I could not find a way to do that within a single call to legend for standard plots.
Here's an option, drawing two separate legends: one with lines and points, one with labels. x.intersp can be used to tweak distance between labels and lines.
plot(cumsum(runif(n = 100)))
# draw legend with lines and point but without labels and box. x.intersp controls horizontal distance between lines
L = legend(x = 'bottom', legend = rep(NA,4), col=1:2, lty=c(1,1,2,2), ncol=2, bty='n', x.intersp=0.5, pch=c(1,2,1,2), inset=0.02)
# use position data of previous legend to draw legend with invisble lines and points but with labels and box. x.intersp controls distance between lines and labels
legend(x = L$rect$left, y = L$rect$top, legend = c('Group A', 'Group B'), col=rep(NA,2), lty=c(1,1), ncol=1, x.intersp = 3, bg = NA)
Check this:
library(lattice)
myPCH <- 15:17
Data <- rnorm(50)
Index <- seq(length(Data))
xyplot(Data ~ Index,
pch = myPCH, col=1:2,
key = list(space = "right", adj=1,
text = list(c("a", "b", "c"), cex=1.5),
points = list(pch = myPCH),
points = list(pch = myPCH,col=2)))
It looks like Victorp answered this in the comments of the original post. The ncol argument in the legend function works for me:
legend(locator(1), legend=c("name1","name2", "name3", "name4"), lty=2, col=c("black", "blue", "dark green", "orange"), ncol=2)
enter image description here
Is there a way to ensure that the box around a plot matches the raster extents exactly? In the following there is a gap above and below or to the left and right of the raster depending on the device proportions:
require(raster)
r = raster()
r[]= 1
plot(r, xlim=c(xmin(r), xmax(r)), ylim=c(ymin(r), ymax(r)))
One element of the problem with raster objects is that asp=1 to ensure proper display. The following basic scatterplot has the same issue when asp=1:
plot(c(1:10), c(1:10), asp=1)
Try vectorplot(r) from the rasterVis package to see what I want the axes to look like.
EDIT:
Solutions need to play nice with SpatialPoints overlays, not showing points outside the specified raster limits:
require(raster)
require(maptools)
# Raster
r = raster()
r[]= 1
# Spatial points
x = c(-100, 0, 100)
y = c(100, 0, 100)
points = SpatialPoints(data.frame(x,y))
plot(r, xlim=c(xmin(r), xmax(r)), ylim=c(ymin(r), ymax(r)))
plot(points, add=T)
You'd probably do best to go with one of the lattice-based functions for plotting spatial raster objects provided by the raster and rasterVis packages. You discovered one of them in vectorplot(), but spplot() or levelplot() better match your needs in this case.
(The base graphics-based plot() method for "RasterLayer" objects just doesn't allow any easy way for you to set axes with the appropriate aspect ratio. For anyone interested, I go into more detail about why that's so in a section at the bottom of the post.)
As an example of the kind of plot that levelplot() produces:
require(raster)
require(rasterVis)
## Create a raster and a SpatialPoints object.
r <- raster()
r[] <- 1:ncell(r)
SP <- spsample(Spatial(bbox=bbox(r)), 10, type="random")
## Then plot them
levelplot(r, col.regions = rev(terrain.colors(255)), cuts=254, margin=FALSE) +
layer(sp.points(SP, col = "red"))
## Or use this, which produces the same plot.
# spplot(r, scales = list(draw=TRUE),
# col.regions = rev(terrain.colors(255)), cuts=254) +
# layer(sp.points(SP, col = "red"))
Either of these methods may still plot some portion of the symbol representing points that fall just outside of the plotted raster. If you want to avoid that possibility, you can just subset your SpatialPoints object to remove any points falling outside of the raster. Here's a simple function that'll do that for you:
## A function to test whether points fall within a raster's extent
inExtent <- function(SP_obj, r_obj) {
crds <- SP_obj#coord
ext <- extent(r_obj)
crds[,1] >= ext#xmin & crds[,1] <= ext#xmax &
crds[,2] >= ext#ymin & crds[,2] <= ext#ymax
}
## Remove any points in SP that don't fall within the extent of the raster 'r'
SP <- SP[inExtent(SP, r), ]
Additional weedy detail about why it's hard to make plot(r) produce snugly fitting axes
When plot is called on an object of type raster, the raster data is (ultimately) plotted using either rasterImage() or image(). Which path is followed depends on: (a) the type of device being plotted to; and (b) the value of the useRaster argument in the original plot() call.
In either case, the plotting region is set up in a way which produces axes that fill the plotting region, rather than in a way that gives them the appropriate aspect ratio.
Below, I show the chain of functions that's called on the way to this step, as well as the call that ultimately sets up the plotting region. In both cases, there appears to be no simple way to alter both the extent and the aspect ratio of the axes that are plotted.
useRaster=TRUE
## Chain of functions dispatched by `plot(r, useRaster=TRUE)`
getMethod("plot", c("RasterLayer", "missing"))
raster:::.plotraster2
raster:::.rasterImagePlot
## Call within .rasterImagePlot() that sets up the plotting region
plot(NA, NA, xlim = e[1:2], ylim = e[3:4], type = "n",
, xaxs = "i", yaxs = "i", asp = asp, ...)
## Example showing why the above call produces the 'wrong' y-axis limits
plot(c(-180,180), c(-90,90),
xlim = c(-180,180), ylim = c(-90,90), pch = 16,
asp = 1,
main = "plot(r, useRaster=TRUE) -> \nincorrect y-axis limits")
useRaster=FALSE
## Chain of functions dispatched by `plot(r, useRaster=FALSE)`
getMethod("plot", c("RasterLayer", "missing"))
raster:::.plotraster2
raster:::.imageplot
image.default
## Call within image.default() that sets up the plotting region
plot(NA, NA, xlim = xlim, ylim = ylim, type = "n", xaxs = xaxs,
yaxs = yaxs, xlab = xlab, ylab = ylab, ...)
## Example showing that the above call produces the wrong aspect ratio
plot(c(-180,180), c(-90,90),
xlim = c(-180,180), ylim = c(-90,90), pch = 16,
main = "plot(r,useRaster=FALSE) -> \nincorrect aspect ratio")
Man, I got stumped and ended up just turning the foreground color off to plot. Then you can take advantage of the fact that the raster plot method calls fields:::image.plot, which lets you just plot the legend (a second time, this time showing the ink!). This is inelegant, but worked in this case:
par("fg" = NA)
plot(r, xlim = c(xmin(r), xmax(r)), ylim = c(ymin(r), ymax(r)), axes = FALSE)
par(new = TRUE,"fg" = "black")
plot(r, xlim = c(xmin(r), xmax(r)), ylim = c(ymin(r), ymax(r)), axes = FALSE, legend.only = TRUE)
axis(1, pos = -90, xpd = TRUE)
rect(-180,-90,180,90,xpd = TRUE)
ticks <- (ymin(r):ymax(r))[(ymin(r):ymax(r)) %% 20 == 0]
segments(xmin(r),ticks,xmin(r)-5,ticks, xpd = TRUE)
text(xmin(r),ticks,ticks,xpd=TRUE,pos=2)
title("sorry, this could probably be done in some more elegant way")
This is way I solved this problem
require(raster)
r = raster()
# default for raster is 180 row by 360 cols = 64800 cells
# fill with some values to make more interesting
r[]= runif(64800, 1, 1000)
# Set margin for text
par(mar=c(2, 6, 6, 2))
# Set some controls for the raster cell colours and legend
MyBrks<-c(0,1,4,16,64,256,1E20)
MyLbls<-c("<1","<4","<16","<64","<256","<Max")
MyClrs<-c("blue","cyan","yellow","pink","purple","red")
# Plot raster without axes or box or legend
# Note xlim and ylim don't seem do much unless you want to trim x and y
plot(r,
col=MyClrs,
axes=FALSE,
box=FALSE,
legend=FALSE
)
# Set up the ranges and intervals for axes - you can get the min max
# using xmin(r) and ymax(r) and so on if you like
MyXFrm <- -180
MyXTo <- 180
MyXStp <- 60
MyYFrm <- -90
MyYTo <- 90
MyYStp <- 30
# Plot the axes
axis(1,tick=TRUE,pos=ymin(r),las=1,at=seq(MyXFrm,MyXTo ,MyXStp ))
axis(2,tick=TRUE,pos=xmin(r),las=1,at=seq(MyYFrm ,MyYTo ,MyYStp ))
# Plot the legend use xpd to plot the legend outside the plot region
par(xpd=TRUE)
legend(MyXTo ,MyYTo ,
legend=MyLbls[1:6],
col= MyClrs,
fill=Clrs[1:6],
bg=rgb(0,0,0,0.85),
cex=0.9,
text.col="white",
text.font=2,
border=NA
)
# Add some axis labels and a title
text(-220,0,"Y",font=2)
text(0,-130,"X",font=2)
text(0,120,"My Raster",font=4,cex=1.5)
I think the best (or simplest) solution is to use image():
library(raster)
# Raster
r = raster()
r[]= rnorm(ncell(r))
# Spatial points
x = c(-100, 0, 100)
y = c(100, 0, 100)
points = SpatialPoints(data.frame(x,y))
# plot
image(r)
plot(points, add=T, pch=16, cex=2)