I have a three layer raster with red, green, and blue channel values in it. I can plot the image with raster::plotRGB, but I need to add axes with UTM coordinates. Coordinates can be added with axes=TRUE, but they are floating in space and look bad. I would like to get the coordinates as they show up in plots created with the raster plot method, or better yet as they appear when using rasterVis::levelplot.
Ultimately, I need to create a raster image with UTM coordinates, a scale bar, and north arrow. This needs to be done using the plotRGB function in the raster package of R, or something with a similar functionality, as I need to assign the colour of each pixel by hand (no colour ramps).
This is an ancient post, but it's a good question so I'll give it an answer.
I'll give an example of how this might be done with rasterVis::levelplot, using the 3-channel R logo raster data that comes with raster.
library(rasterVis)
b <- brick(system.file("external/rlogo.grd", package="raster"))
Plotting three-channel RGB rasters with levelplot
Create an empty raster with the same dimensions and extent as the brick.
r <- raster(b)
Calculate the hexadecimal colours corresponding to the values of the RGB channels, and coerce to factor.
cols <- factor(rgb(b[], maxColorValue=255))
Assign these factor values to the raster's cells.
r[] <- cols
Plot with levelplot, extracting the hexadecimal colours from the levels of cols and passing them to col.regions.
levelplot(r, col.regions=as.character(levels(cols)), colorkey=FALSE)
Adding north arrow and scale bar
For the north arrow and the scale bar, we will look to #OscarPerpiñán's docs.
levelplot(r, col.regions=as.character(levels(cols)), colorkey=FALSE) +
layer(SpatialPolygonsRescale(layout.north.arrow(), offset = c(5, 10), scale = 10)) +
layer({
xs <- seq(5, 25, by=5)
grid.rect(x=xs, y=5,
width=5, height=2,
gp=gpar(fill=rep(c('transparent', 'black'), 2)),
default.units='native')
grid.text(x=xs-2.5, y=8, seq(0, 400, by=100),
gp=gpar(cex=0.7),
default.units='native')
})
I'll leave it up to you to calculate the true distance (passed to grid.text) associated with the width, in map units, of the rectangles.
Related
I have a raster file that I have exported from ArcGIS as a georeferenced tif file.
The raster will be used as a background map in mapview because the mapview background layers CartoDB.Positron, CartoDB.DarkMatter, OpenStreetMap , Esri.WorldImagery and OpenTopoMapdont dont give the required background at the zoom level i need.
First I read my raster brick into R using the raster package and then I plot using plotRGB.
library(raster)
library(mapview)
r<-brick("KYENGERA2.tif")#raster exported from Arcgis read. has 4 bands
r<-subset(r,1:3) #Retain only layers/bands with RGB
plotRGB(r, 1,2,3, stretch='lin')
The result is as expected
I then create a new raster r2 with values 0 - 255 and assign a colortable based on the rgb values in r. I plot it in two difefrent ways and i get the same result.
pct <- rgdal::SGDF2PCT(as(r, "SpatialGridDataFrame"))
r2 <- setValues(raster(r), pct$idx-1) #create a new raster with values 0 - 255
colortable(r2) <- pct$ct #define 256 colors
plotRGB(r, 1,2,3, stretch='lin')
plot(r2)#plot(r2,col=r2#legend#colortable)
Here is the result and it is much better
However when I try mapview, the result is not as expected.
mapview(r2, col.regions = pct$ct,na.col="transparent")
Can anyone help me solve this issue with mapview?
I have taken a look at this but i couldnt figure out a solution.
You can simply use mapview::viewRGB()
viewRGB(r, 1, 2, 3, method = "ngb", quantiles = c(0, 1), maxpixels = ncell(r))
Outline of the Issue
I am sorry for asking a simple question, but I am new to R and I am experiencing difficulties performing tasks with maps.
I have a collection of longitude and latitude GPS points in decimal form, which were collected in the field. My aim is to plot these GPS points onto a map of Sri Lanka, which I extracted from GADM resources.
After running the code, the southern tip of Sri Lanka is protruding from the top middle of the longitude/latitude grid box rather than the whole image of Sri Lanka being visible within the longitude/latitude grid box (see image 2).
Problem:
I can produce the map of Sri Lanka independently (see image 2), and the longitude/latitude grid box separately (see image 1). However, I am having trouble plotting the map of Sri Lanka inside the latitude/longitude grid box, in conjunction with plotting the GPS points within the grid box in the correct positions that the data was collected in the field.
The desired output is evidenced in image 3 (see below). I am trying to place image 1 inside the grid box with the correct longitude/latitude scale for Sri Lanka on the edge of the grid box. Finally, I would like to plot the GPS points on the map, just like the provided example in image 3.
If anyone would be able to help me, I would be incredibly grateful!
I really cannot figure out what is going wrong here due to my lack of knowledge and after many hours of trying different R code combinations in order to solve the issue by attempting to reproduce this stack overflow question and by following this exercise on species distribution modeling.
Very kind regards.
R-code
##Libraries that are going to be used:
library("sp")
library("raster")
library("maptools")
library("rgdal")
library("dismo")
library("spatialEco")
library("ggplot2")
library("dplyr")
###Open the directory pathway
Blue.whale<-readr::read_csv("Blue_Whale_GPS_Best.csv")
summary(Blue.whale)
##Plotting the map of Sri Lanka
bioclim1.data <- getData('GADM', country='LKA', level=1)
Sri_Lanka<-plot(bioclim1.data, main="Adm. Boundaries Sri Lanka Level 0")
###My attempt at creating a longitude/latitude grid box
Sri.Lanka.bbox<-bbox(Blue.whale)
xlim <- c(min(Sri.Lanka.bbox[1,1]), max(Sri.Lanka.bbox[1,2]))
ylim <- c(min(Sri.Lanka.bbox[2,1]), max(Sri.Lanka.bbox[2,2]))
###Plot the longitude/latitude grid box
dev.new()
plot(Sri_Lanka, xlim=xlim, ylim=ylim, add=T)
##Plot map
par(mfrow=c(1,1))
dev.new()
####Convert the format of the data from factors to numeric
Latitude<-as.numeric(Blue.whale$Latitude)
Longitude<-as.numeric(Blue.whale$Longitude)
##To make species distribution modeling more streamlined, it is useful to have an
##idea of how widely our species is geographically distributed. We are going to find
##general latitudinal and longitudinal boundaries and store this information:
# Determine geographic extent of our data
max.lat <- ceiling(max(Blue.whale$Latitude))
min.lat <- floor(min(Blue.whale$Latitude))
max.lon <- ceiling(max(Blue.whale$Longitude))
min.lon <- floor(min(Blue.whale$Longitude))
geographic.extent <- extent(x = c(min.lon, max.lon, min.lat, max.lat))
# Plot the base map
dev.new()
plot(bioclim1.data,
xlim = c(min.lon, max.lon),
ylim = c(min.lat, max.lat),
axes = TRUE,
col = "grey95")
# Add the points for individual observation
points(x = Blue.whale$Longitude,
y = Blue.whale$Latitude,
col = "olivedrab",
pch = 15,
cex = 0.50)
Image 1:
Image 2:
Image 3:
For Example 3 they have cropped the map of USA to focus on where that species occurred, whereas you want to show where the whale sightings occurred in relation to the whole country of Sri Lanka. To show both the whole country and all of the sightings you need to change your plot limits to match the extremities of the two data sources. This code should produce your desired plot, you can add ceiling / floor arguments to improvement aesthetics if needed:
##get bounding box of Sri Lanka shapefile
bb=bioclim1.data#bbox
plot(bioclim1.data,
xlim = c(min(c(min.lon,bb[1,1])), max(c(max.lon,bb[1,2]))),
ylim = c(min(c(min.lat,bb[2,1])), max(c(max.lat,bb[2,2]))),
axes = TRUE,
col = "grey95")
# Add the points for individual observation
points(x = Blue.whale$Longitude,
y = Blue.whale$Latitude,
col = "olivedrab",
pch = 15,
cex = 0.50)
I am trying to plot a global map using latitude, longitude and grid data in R. For this I am using image and image.plot functions. Additionally I need to overlay global coastline for land area. However I am not sure how to place the map exactly over my image of gridded data. Map is appearing bit shifted to the left in the console and that part is not visible either. See sample code below with random grid data.
remove(list=ls())
library(fields)
library(maps)
grid_lon<-c(0.5:1:359.5)
grid_lat<-c(-89.5:89.5)
temp1<-matrix(data = rexp(200, rate = 10), nrow = 360, ncol = 180)#random matrix
zlim=c(0,0.25)
par(oma=c( 3,0,0,0))# c(bottom, left, top, right)#plot margins
image(grid_lon,grid_lat,temp1,axes=FALSE,xlab='',ylab='')
map("world", fill=TRUE, col="white", bg="white", ylim=c(-90, 90),add=TRUE)
title(main ='Main title')
image.plot(zlim=zlim,legend.only=TRUE,horizontal=TRUE,legend.mar=0.4,legend.shrink=0.4,legend.width=0.4,nlevel=64,axis.args = list(cex.axis =1,at=zlim, labels=zlim,mgp=c(1, 0, 0),tck=0),smallplot=c(.25,.72, 0,.030),
legend.args=list( text=expression(textstyle(atop('anomaly',
paste('(meters)')),cex.main=1.2)),cex=1.2, side=1, line=1.6)
)#end image.plot
box()
In general, when working with maps it is preferable to use spatial objects, for which a projection method can be defined. The coherence with the map is then better guaranteed. Since you are working with a filled grid, an obvious choice is to use a raster from package raster. Your code would then become:
require (raster)
require (maps)
temp1<-matrix(data = rexp(180*360, rate = 10), nrow = 360, ncol = 180) #random matrix
r<-raster(temp1,xmn=-179.5,xmx=179.5,ymn=-89.5,ymx=89.5,crs="+proj=longlat +datum=WGS84")
plot(r)
map("world",add=T,fill=TRUE, col="white", bg="white")
EDIT
This code does not take into account that the data comes as a 360*180 matrix, while it is desirable to plot (map) a 180*360 matrix. Transposing is risky because it may result in an upside-down image. In order to be sure that the right coordinates are associated with the right values, we can explicitly associate them, and afterwards transform into a spatial object. The for-loop doing this is in the code below is slow, maybe it can be made more efficient, but it does the job.
require (raster)
require (maps)
# basic data, as in code given
grid_lon<-seq(0.5,359.5,1)
grid_lat<-seq(-89.5,89.5,1)
temp1<-matrix(data = rexp(200, rate = 10), nrow = 360, ncol = 180)#random matrix
# transform into data frame, where coords are associated to values
tt<-data.frame(lon=rep(NA,64800),lat=rep(NA,64800),z=rep(NA,64800))
ct<-0
for (i in 1:360){
for (j in 1:180){
ct<-ct+1
tt$lon[ct]<-grid_lon[i]
tt$lat[ct]<-grid_lat[j]
tt$z[ct]<-temp1[i,j]
}
}
# transform to spatial structure
coordinates(tt)<- ~lon+lat
# make spatial structure gridded
gridded(tt)<-TRUE
# transform to raster
r<-raster(tt)
projection(r)<-crs("+proj=longlat +datum=WGS84")
# plot
plot(r)
map("world2",add=T,fill=TRUE, col="white", bg="white")
I found the answer after few attempts and a tip from colleague. What needs to be done is shift the longitude grid from 0:359 to -179.5:179.5 using following commands after grid_lon is declared:
indexes_to_shift<-180
grid_lon[grid_lon>=180]<-grid_lon[grid_lon>=180]-360
grid_lon<-c(tail(grid_lon, indexes_to_shift), head(grid_lon, indexes_to_shift))
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.
I am trying map an image onto a sphere, following this example in the persp3d documentation:
lat <- matrix(seq(90,-90, len=50)*pi/180, 50, 50, byrow=TRUE)
long <- matrix(seq(-180, 180, len=50)*pi/180, 50, 50)
r <- 6378.1 # radius of Earth in km
x <- r*cos(lat)*cos(long)
y <- r*cos(lat)*sin(long)
z <- r*sin(lat)
open3d()
persp3d(x, y, z, col="white",
texture=system.file("textures/worldsmall.png",package="rgl"),
specular="black", axes=FALSE, box=FALSE, xlab="", ylab="", zlab="",
normal_x=x, normal_y=y, normal_z=z)
I will eventually want to add objects on specific points on the sphere, and so need to know what pixel coordinates in the image file correspond to what x,y,z (or lat, long) values. In other words, what map projection is persp3d assuming of worldsmall.png? (I am not expecting it to map nicely onto a traditional map projection, as persp3d is obviously not limited to spheres, but there's got to be some algorithm for converting one set of coordinates to the other.)