How to properly project and plot raster in R - r

I have a raster in an equal area Behrmann projection and I would like to project it to the Mollweide projection and plot.
When I do this with the following code, however, the plotting doesn't seem right, as the map extends to the sides, and there are outlines of various landmasses where I wouldn't expect them.Also, the map extends beyond the plot window.
Can anyone please help me get this to plot nicely?
Thanks!
The data file used can be downloaded from this link.
Here is the code I have so far:
require(rgdal)
require(maptools)
require(raster)
data(wrld_simpl)
mollCRS <- CRS('+proj=moll')
behrmannCRS <- CRS('+proj=cea +lat_ts=30')
sst <- raster("~/Dropbox/Public/sst.tif", crs=behrmannCRS)
sst_moll <- projectRaster(sst, crs=mollCRS)
wrld <- spTransform(wrld_simpl, mollCRS)
plot(sst_moll)
plot(wrld, add=TRUE)

Alright, since the example at this page seems to work, I tried to mimic it as much as possible. I think problems arise because the far left and far right side of the raster image overlap. Cropping and an intermediate reprojection to Lat-Lon as in the example seem to solve your problem.
Perhaps this workaround can be a basis for a more elegant solution that directly addresses the problem, as it is not benificial to reproject a raster twice.
# packages
library(rgdal)
library(maptools)
library(raster)
# define projections
mollCRS <- CRS('+proj=moll')
behrmannCRS <- CRS('+proj=cea +lat_ts=30')
# read data
data(wrld_simpl)
sst <- raster("~/Downloads/sst.tif", crs=behrmannCRS)
# crop sst to extent of world to avoid overlap on the seam
world_ext = projectExtent(wrld_simpl, crs = behrmannCRS)
sst_crop = crop(x = sst, y=world_ext, snap='in')
# convert sst to longlat (similar to test file)
# somehow this gets rid of the unwanted pixels outside the ellipse
sst_longlat = projectRaster(sst_crop, crs = ('+proj=longlat'))
# then convert to mollweide
sst_moll <- projectRaster(sst_longlat, crs=mollCRS, over=T)
wrld <- spTransform(wrld_simpl, mollCRS)
# plot results
plot(sst_moll)
plot(wrld, add=TRUE)

Related

R raster::crop() The upper boundary of my cropped raster is always horizontal- why?

I'm trying to crop a large multipolygon shapefile by a single, smaller polygon. It works using st_intersection, however this takes a very long time, so I'm instead trying to convert the multipolygon to a raster, and crop that raster by the smaller polygon.
## packages - sorry if I've missed any!
library(raster)
library(rgdal)
library(fasterize)
library(sf)
## load files
shp1 <- st_read("pathtoshp", crs = 27700) # a large multipolygon shapefile to crop
### image below created using ggplot- ignore the black boundaries!
shp2 <- st_read("pathtoshp", crs = 27700) # a single, smaller polygon shapefile, to crop shp1 by
plot(shp2)
## convert to raster (faster than st_intersection)
projection1 <- CRS('+init=EPSG:27700')
rst_template <- raster(ncols = 1000, nrows = 1000,
crs = projection1,
ext = extent(shp1))
rst_shp1 <- fasterize(shp1, rst_template)
plot(rst_shp1)
rst_shp2 <- crop(rst_shp1, shp2)
plot(rst_shp2)
When I plot shp2, the upper boundary is flat, rather than fitting the true boundary of the shp2 polygon.
Any help would be greatly appreciated!
Maybe try raster::mask() instead of crop(). crop() uses the second argument as an extent with which to crop a raster; i.e. it's taking the bounding box (extent) of your second argument and cropping that entire rectangle from your raster.
Something important to understand about raster objects is that they are all rectangular. The white space you see surrounding your shape are just NA values.
raster::mask() will take your original raster, and a spatial object (raster, sf, etc.) and replace all values in your raster which don't overlap with your spatial object to NA (by default, you can supply other replacement values). Though I will say, mask() will likely also take awhile to run, so you may be better off just sticking with sf objects.
I would suggest moving to the "terra" package (faster and easier to use than "raster").
Here is an example.
library(terra)
r <- rast(system.file("ex/elev.tif", package="terra"))
v <- vect(system.file("ex/lux.shp", package="terra"))[4]
x <- crop(r, v)
plot(x); lines(v)
As edixon1 points out, a raster is always rectangular. If you want to set cells outside of the polygon to NA, you can do
x <- crop(r, v, mask=TRUE)
plot(x); lines(v)
In this example it makes no sense, but you could first rasterize
x <- crop(r, v)
y <- rasterize(v, x)
m <- mask(x, y)
plot(m); lines(v)
I am not sure if this answers your question. But if it does not, then please edit your question to make it reproducible, for example using the example data above.

Plotting netcdf file with raster package leads to distorted representation, R

I want to properly plot this netcdf file: http://www.filedropper.com/sshgridsv16092015060412nc (Downloaded originally from here: https://opendap.jpl.nasa.gov/opendap/allData/merged_alt/L4/cdr_grid/contents.html)
But run into issues:
I should be able to just plot the raster (the SLA variable):
library(RNetCDF)
library(raster)
library(maptools)
d <- raster("ssh_grids_v1609_2015060412.nc.nc4", varname = "SLA")
plot(d)
#plot SLA
But the result is very weird as you can see with the provided file.
Especially when plotting a world map on top:
data(wrld_simpl)
plot(wrld_simpl, add = T)
They don't match at all :/
So I thought maybe the problem is with the longitude (ranging from 4.839944e-09 to 360)
Then I read that raster::rotate(d)
should be perfect for that (to get longitude to -180 to 180), but it won't let me. I get this warning message:
Warning message:
In .local(x, ...) :
this does not look like an appropriate object for this function
and
plot(d)
still looks the same.
Any advice would be greatly appreciated!
Cheers
The Netcdf file is not only "rotated" in longitude but x and y are also at the wrong position. The way it has been entered in the netcdf is not usual apparently.
I downloaded the netcdf directly on the OpenDap server because your filedropper link seems to be corrupt.
Anyway, here is my proposition:
library(raster)
library(maptools)
d <- raster("ssh_grids_v1609_2015060412.nc.nc4", varname = "SLA")
# transpose x to y and double flip the map
m.r <- flip(flip(t(d), direction = "y"), direction = "x")
# then rotate from 0:360 to -180:180
rm.r <- rotate(m.r)
data(wrld_simpl)
plot(rm.r)
plot(wrld_simpl, add = T)

Merging 2 raster images into one plot using the `raster` package

I would like to add an enlarged portion of my map to the original map and have as the final product, one map that shows both the original map, and also the enlarged/zoomed portion. Using the meuse dataset as an example:
library(sp)
library(lattice)
data(meuse)
coordinates(meuse)=~x+y
gridded(meuse)<-TRUE
rasters.m<-list()
for (i in 1:12){
rasDF <- raster(meuse, layer=i)
rasters.m[[i]]<-rasDF
}
stack.sp<-stack(rasters.m)
plot(stack.sp) # gives a gridded view of the stacked rasters. But now I would like to zoom in..
zoom.ent<-zoom(stack.sp,1) # The zoomed in portion appears as a new window, with the boundaries of the zoomed area highlighted in red on the original map.
I am not sure if there is a command in the raster or rasterVIS packages that would allow one to add the zoomed in part of the raster onto the original map. I have tried the par function but that doesn't work. Any suggestions would be welcomed.
This is more or less the same question you asked here. For Raster* objects you have to use the shift function. The result can be combined with the +.trellis function of the latticeExtra package:
library(raster)
library(rasterVis)
f <- system.file("external/test.grd", package="raster")
r <- raster(f)
rZoom <- crop(r, extent(180000, 181000, 330000, 331500))
displaced <- shift(rZoom, x = -1200, y = 2000)
levelplot(r) + levelplot(displaced)

How to plot contours on a map with ggplot2 when data is on an irregular grid?

Sorry for the wall of text, but I explain the question, include the data, and provide some code :)
QUESTION:
I have some climate data that I want to plot using R. I am working with data that is on an irregular, 277x349 grid, where (x=longitude, y=latitude, z=observation). Say z is a measure of pressure (500 hPa height (m)). I tried to plot contours (or isobars) on top of a map using the package ggplot2, but I am having some trouble due to the structure of the data.
The data comes from a regular, evenly spaced out 277x349 grid on a Lambert conformal projection, and for each grid point we have the actual longitude, latitude, and pressure measurement. It is a regular grid on the projection, but if I plot the data as points on a map using the actual longitude and latitude where the observations were recorded, I get the following:
I can make it look a little nicer by translating the rightmost piece to the left (maybe this can be done with some function, but I did this manually) or by ignoring the rightmost piece. Here is the plot with the right piece translated to the left:
(An aside) Just for fun, I tried my best to re-apply the original projection. I have some of the parameters for applying the projection from the data source, but I do not know what these parameters mean. Also, I do not know how R handles projections (I did read the help files...), so this plot was produced through some trial and error:
I tried to add the contour lines using the geom_contour function in ggplot2, but it froze my R. After trying it on a very small subset of the data, I found that out after some googling that ggplot was complaining because the data was on an irregular grid. I also found out that that is the reason geom_tile was not working. I am guessing that I have to make my grid of points evenly spaced out - probably by projecting it back into the original projection (?), or by evenly spacing out my data by either sampling a regular grid (?) or by extrapolating between points (?).
My questions are:
How can I draw contours on top of the map (preferably using ggplot2) for my data?
Bonus questions:
How do I transform my data back to a regular grid on the Lambert conformal projection? The parameters of the projection according to the data file include (mpLambertParallel1F=50, mpLambertParallel2F=50, mpLambertMeridianF=253, corners, La1=1, Lo1=214.5, Lov=253). I have no idea what these are.
How do I center my maps so that one side is not clipped (like in the first map)?
How do I make the projected plot of the map look nice (without the unnecessary parts of the map hanging around)? I tried adjusting the xlim and ylim, but it seems to apply the axes limits before projecting.
DATA:
I uploaded the data as rds files on Google drive. You can read in the files using the readRDS function in R.
lat2d: The actual latitude for the observations on the 2d grid
lon2d: The actual longitude for the observations on the 2d grid
z500: The observed height (m) where pressure is 500 millibars
dat: The data arranged in a nice data frame (for ggplot2)
I am told that the data is from the North American Regional Reanalysis data base.
MY CODE (THUS FAR):
library(ggplot2)
library(ggmap)
library(maps)
library(mapdata)
library(maptools)
gpclibPermit()
library(mapproj)
lat2d <- readRDS('lat2d.rds')
lon2d <- readRDS('lon2d.rds')
z500 <- readRDS('z500.rds')
dat <- readRDS('dat.rds')
# Get the map outlines
outlines <- as.data.frame(map("world", plot = FALSE,
xlim = c(min(lon2d), max(lon2d)),
ylim = c(min(lat2d), max(lat2d)))[c("x","y")])
worldmap <-geom_path(aes(x, y), inherit.aes = FALSE,
data = outlines, alpha = 0.8, show_guide = FALSE)
# The layer for the observed variable
z500map <- geom_point(aes(x=lon, y=lat, colour=z500), data=dat)
# Plot the first map
ggplot() + z500map + worldmap
# Fix the wrapping issue
dat2 <- dat
dat2$lon <- ifelse(dat2$lon>0, dat2$lon-max(dat2$lon)+min(dat2$lon), dat2$lon)
# Remake the outlines
outlines2 <- as.data.frame(map("world", plot = FALSE,
xlim = c(max(min(dat2$lon)), max(dat2$lon)),
ylim = c(min(dat2$lat), max(dat2$lat)))[c("x","y")])
worldmap2 <- geom_path(aes(x, y), inherit.aes = FALSE,
data = outlines2, alpha = 0.8, show_guide = FALSE)
# Remake the variable layer
ggp <- ggplot(aes(x=lon, y=lat), data=dat2)
z500map2 <- geom_point(aes(colour=z500), shape=15)
# Try a projection
projection <- coord_map(projection="lambert", lat0=30, lat1=60,
orientation=c(87.5,0,255))
# Plot
# Without projection
ggp + z500map2 + worldmap2
# With projection
ggp + z500map + worldmap + projection
Thanks!
UPDATE 1
Thanks to Spacedman's suggestions, I think I have made some progress. Using the raster package, I can directly read from an netcdf file and plot the contours:
library(raster)
# Note: ncdf4 may be a pain to install on windows.
# Try installing package 'ncdf' if this doesn't work
library(ncdf4)
# band=13 corresponds to the layer of interest, the 500 millibar height (m)
r <- raster(filename, band=13)
plot(r)
contour(r, add=TRUE)
Now all I need to do is get the map outlines to show under the contours! It sounds easy, but I'm guessing that the parameters for the projection need to be inputted correctly to do things properly.
The file in netcdf format, for those that are interested.
UPDATE 2
After much sleuthing, I made some more progress. I think I have the proper PROJ4 parameters now. I also found the proper values for the bounding box (I think). At the very least, I am able to roughly plot the same area as I did in ggplot.
# From running proj +proj=lcc +lat_1=50.0 +lat_2=50.0 +units=km +lon_0=-107
# in the command line and inputting the lat/lon corners of the grid
x2 <- c(-5628.21, -5648.71, 5680.72, 5660.14)
y2 <- c( 1481.40, 10430.58,10430.62, 1481.52)
plot(x2,y2)
# Read in the data as a raster
p4 <- "+proj=lcc +lat_1=50.0 +lat_2=50.0 +units=km +lon_0=-107 +lat_0=1.0"
r <- raster(nc.file.list[1], band=13, crs=CRS(p4))
r
# For some reason the coordinate system is not set properly
projection(r) <- CRS(p4)
extent(r) <- c(range(x2), range(y2))
r
# The contour map on the original Lambert grid
plot(r)
# Project to the lon/lat
p <- projectRaster(r, crs=CRS("+proj=longlat"))
p
extent(p)
str(p)
plot(p)
contour(p, add=TRUE)
Thanks to Spacedman for his help. I will probably start a new question about overlaying shapefiles if I can't figure things out!
Ditch the maps and ggplot packages for now.
Use package:raster and package:sp. Work in the projected coordinate system where everything is nicely on a grid. Use the standard contouring functions.
For map background, get a shapefile and read into a SpatialPolygonsDataFrame.
The names of the parameters for the projection don't match up with any standard names, and I can only find them in NCL code such as this
whereas the standard projection library, PROJ.4, wants these
So I think:
p4 = "+proj=lcc +lat_1=50 +lat_2=50 +lat_0=0 +lon_0=253 +x_0=0 +y_0=0"
is a good stab at a PROJ4 string for your data.
Now if I use that string to reproject your coordinates back (using rgdal:spTransform) I get a pretty regular grid, but not quite regular enough to transform to a SpatialPixelsDataFrame. Without knowing the original regular grid or the exact parameters that NCL uses we're a bit stuck for absolute precision here. But we can blunder on a bit with a good guess - basically just take the transformed bounding box and assume a regular grid in that:
coordinates(dat)=~lon+lat
proj4string(dat)=CRS("+init=epsg:4326")
dat2=spTransform(dat,CRS(p4))
bb=bbox(dat2)
lonx=seq(bb[1,1], bb[1,2],len=277)
laty=seq(bb[2,1], bb[2,2],len=349)
r=raster(list(x=laty,y=lonx,z=md))
plot(r)
contour(r,add=TRUE)
Now if you get a shapefile of your area you can transform it to this CRS to do a country overlay... But I would definitely try and get the original coordinates first.

Is it possible to overlay SpatialLinesDataFrame and SpatialPolygonDataFrame

I am wondering if this is possible to do this R .
I have one data as SpatialLinesDataFrame and another as spatialPolygonDataFrame. Is it possible to overlay these two data ?
When I try to overlay these I get the following error:
jd <- overlay(res,hello)
Error in function (classes, fdef, mtable) : unable to find an inherited method for function
‘overlay’ for signature ‘"SpatialLinesDataFrame", "SpatialPolygonsDataFrame"’
In the above code res is the SpatialLinesDataFrame and hello is SpatialPolygonDataFrame.
I have an shapefile and then I have data points with x,yand z
coordinates. I want to show the contour lines on the shapefile.
The procedure I used is using akima package to do the interpolation. The
code I used to interpolate is
fld <- interp(x,y,z)
Then I changed this to spatial object by using following code:
res <-ContourLines2SLDF(contourLines(fld))
The above command would store the contourlines as spatial data.
Then I read the shapefile and I plot both shapefile and res as follows:
p1 <-
spplot(hello,sp.layout=list(list("sp.lines",res)),col="blue",lwd=0,fill="grey",colorkey=F)
p1
"hello" is my shapefile and "res" is the object I created as shown above.
The problem is contour stored in "res" extends beyond the shapefile. So I
want to clip that contour with the shapefile and only display the contour
within the shapefile area.
So I am looking for a way to clip the contour layer with the polygon layer.
I have attached the image I got with my code.
In the image you can see the lines out of the shapefile. I also want to know
how can I display the contour levels on the map.
Thank you so much.
Jdbaba
I also want to know what does overlay does exactly. Does it intersect the area of both the data ?
Thank you.
It sounds like you're trying to clip your lines to the polygon extent. Use gIntersection from the rgeos package. Here's a reproducible example:
library(rgeos)
xx <- SpatialPoints(coords=matrix(data=c(0,0), nrow=1))
xx <- gBuffer(spgeom=xx, width=1)
yy <- SpatialLines(list(Lines(Line(matrix(c(-1,1,-1,1), nrow=2)), ID=1)))
zz <- gIntersection(yy, xx)
You can overlay the plot like so:
plot(xx)
plot(zz, add = TRUE, col = "blue")
Noah's answer has worked quite well for me. However, the output of his answer is a SpatialLines object, which cannot be saved as a shape file.
My two cents here is about how you can convert your SpatialLines object into a SpatialLinesDataFrame and save it as a shape file.
res.df <- fortify(res) # create data frame of res, your original SpatialLinesDataFrame
data <- data.frame(id = unique(res.df$id)) # get ids of road segments
rownames(data) <- data$id
# transform SpatialLines object into SpatialLinesDataFrame
zzSpatialLineDF <- SpatialLinesDataFrame(zz, data) # convert zz object keeping road ids
# 5 Save Shape File to your working directory
writeOGR(zzSpatialLineDF, dsn = '.', layer ='zzSpatialLineDF', driver = 'ESRI Shapefile')

Resources