How to overlay a transparent raster on ggmap? - r

I'm trying to find a way to add alpha function for overlaying a raster layer on ggmap. There are some suggestions such as converting raster to shapefile, but I want to use the raster file. Is there any way for adding alpha and make the raster layer transparent?
library(raster)
library(ggmap)
location <- get_map(location = c(lon =22, lat =40), zoom = 6, maptype = "hybrid")
# get the raster layer
tmin <- raster::getData('worldclim', var='tmin', res=0.5, lon=22, lat=40)[[1]]
ggmap(location)+inset_raster(as.raster(tmin), xmin = tmin#extent[1], xmax = tmin#extent[2],ymin =tmin#extent[3], ymax = tmin#extent[4])+ coord_cartesian()

Some time ago I modified the rasterVis::gplot function to retrieve data to be plotted using classical {ggplot2} functions like geom_tile. Function is gplot_data in my {SDMSelect} package on github. If you do not want to get the complete package, you can directly use the function:
#' Transform raster as data.frame to be later used with ggplot
#' Modified from rasterVis::gplot
#'
#' #param x A Raster* object
#' #param maxpixels Maximum number of pixels to use
#'
#' #details rasterVis::gplot is nice to plot a raster in a ggplot but
#' if you want to plot different rasters on the same plot, you are stuck.
#' If you want to add other information or transform your raster as a
#' category raster, you can not do it. With `SDMSelect::gplot_data`, you retrieve your
#' raster as a data.frame that can be modified as wanted using `dplyr` and
#' then plot in `ggplot` using `geom_tile`.
#' If Raster has levels, they will be joined to the final tibble.
#'
#' #export
gplot_data <- function(x, maxpixels = 50000) {
x <- raster::sampleRegular(x, maxpixels, asRaster = TRUE)
coords <- raster::xyFromCell(x, seq_len(raster::ncell(x)))
## Extract values
dat <- utils::stack(as.data.frame(raster::getValues(x)))
names(dat) <- c('value', 'variable')
dat <- dplyr::as.tbl(data.frame(coords, dat))
if (!is.null(levels(x))) {
dat <- dplyr::left_join(dat, levels(x)[[1]],
by = c("value" = "ID"))
}
dat
}
Then, you add your raster with classical ggplot2::geom_tile:
library(raster)
library(ggmap)
location <- get_map(location = c(lon =22, lat =40), zoom = 6, maptype = "hybrid")
# get the raster layer
tmin <- raster::getData('worldclim', var='tmin', res=0.5, lon=22, lat=40)[[1]]
# library(SDMSelect) # If you want
tmin_data <- gplot_data(tmin, maxpixels = 10000) # Choose number of pixels
ggmap(location) +
geom_tile(data = tmin_data, aes(x, y, fill = value), alpha = 0.5) +
scale_fill_gradient(low = "green", high = "red")

Related

Dealing with raster data in ggplot

I have a vector spatial data for the boundary of a county and topographical data for the same county in raster format.
I need to create a map with ggplot so that it only shows those data in raster format that are within the county boundaries (which in turn are in a vector spatial file).
In other words, I need to remove raster data that is outside the county outline. Is it possible to do this with ggplot?
Reproducible example:
# load packages
library(elevatr)
library(terra)
library(geobr)
# get the municipality shapefile (vectorized spatial data)
municipality_shape <- read_municipality(code_muni = 3305802)
plot(municipality_shape$geom)
# get the raster topographical data
prj_dd <- "EPSG:4674"
t <- elevatr::get_elev_raster(locations = municipality_shape,
z = 10,
prj = prj_dd)
obj_raster <- rast(t)
plot(obj_raster)
# create the ggplot map
df_tere_topo <- obj_raster %>%
as.data.frame(xy = TRUE) %>%
rename(altitude = file40ac737835de)
ggplot()+
geom_raster(data = df_tere_topo, aes(x = x, y = y, fill = `altitude`))+
geom_sf(municipality_shape, mapping = aes(), color = 'red', fill = NA)
Edited
See the comments, use terra::crop() and terra::mask() instead:
# load packages
library(elevatr)
library(terra)
library(geobr)
library(dplyr)
library(ggplot2)
# Use tidyterra
library(tidyterra)
# get the municipality shapefile (vectorized spatial data)
municipality_shape <- read_municipality(code_muni = 3305802)
# get the raster topographical data
prj_dd <- "EPSG:4674"
t <- elevatr::get_elev_raster(
locations = municipality_shape,
z = 10,
prj = prj_dd
)
obj_raster <- rast(t)
# Crop + Mask
obj_raster_mask <- crop(obj_raster, vect(municipality_shape)) %>%
mask(vect(municipality_shape))
# create the ggplot map
# using tidyterra
ggplot() +
geom_spatraster(data = obj_raster_mask) +
geom_sf(municipality_shape, mapping = aes(), color = "white", fill = NA) +
# Not plotting NAs of the raster
scale_fill_continuous(na.value = NA) +
labs(fill="altitude")
Original answer
I think the most efficient way is to crop your SpatRaster to the extent of your vector data. With this approach the plotting is more efficient since you are not using data that you don't wnat to plot.
Another option is to set limits in coord_sf().
On this reprex I am using the package tidyterra as well, that has some functions to work with ggplot2 + terra (I am the developer of tidyterra):
# load packages
library(elevatr)
library(terra)
library(geobr)
library(dplyr)
library(ggplot2)
# Use tidyterra
library(tidyterra)
# get the municipality shapefile (vectorized spatial data)
municipality_shape <- read_municipality(code_muni = 3305802)
# get the raster topographical data
prj_dd <- "EPSG:4674"
t <- elevatr::get_elev_raster(
locations = municipality_shape,
z = 10,
prj = prj_dd
)
obj_raster <- rast(t)
# Option1: Crop first
obj_raster_crop <- crop(obj_raster, vect(municipality_shape))
# create the ggplot map
# using tidyterra
ggplot() +
geom_spatraster(data = obj_raster_crop) +
geom_sf(municipality_shape, mapping = aes(), color = "white", fill = NA) +
coord_sf(expand = FALSE) +
labs(fill="altitude")
# Option 2: Use limits and no crop
lims <- sf::st_bbox(municipality_shape)
ggplot() +
geom_spatraster(
data = obj_raster,
# Avoid resampling
maxcell = ncell(obj_raster)
) +
geom_sf(municipality_shape, mapping = aes(), color = "white", fill = NA) +
coord_sf(
expand = FALSE,
xlim = lims[c(1, 3)],
ylim = lims[c(2, 4)]
) +
labs(fill="altitude")

Map with grid cells coloured in function of point density (R, ggplot)

I'm trying to create a map of Europe with grid cells coloured based on the number of records within a cell. Here I attach an image as illustrative of the desired output (see Fig 1 of https://doi.org/10.3897/phytokeys.74.9723).
In order to produce this image I have developed a minimal reproducible example with random points distributed across Europe. I have been able to produce a similar figure with levelplot but I'm particulary interested in doing this with ggplot as it will allow further customising. Is it possible to do produce a similar figure with ggplot? And if so, any advice of what path should I follow?
Note: The size of the grids/cells is irrelevant at the moment but I'll adjust it depending on point density. All of them have to be the same size as in the first example and they only will differ on the pattern of colour.
#Load libraries
library(rgdal) #v1.5-28
library(rgeos) #v.0.5-9
library(ggplot2) # 3.3.5
library(rworldmap) #plot worldmap v.1.3-6
library(dplyr) #v.1.0.7
#Create dataframe of coordinates that fall in Europe
coord <- data.frame(cbind(runif(1000,-15,45),runif(1000,30,75)))
colnames(coord) <- c("long","lat")
#Exlude ocean points following this post
URL <- "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/physical/ne_110m_ocean.zip"
fil <- basename(URL)
if (!file.exists(fil)) download.file(URL, fil)
fils <- unzip(fil)
oceans <- readOGR(grep("shp$", fils, value=TRUE), "ne_110m_ocean",
stringsAsFactors=FALSE, verbose=FALSE)
europe_coord <- data.frame(long = coord$long,
lat = coord$lat)
coordinates(europe_coord) <- ~long+lat
proj4string(europe_coord) <- CRS(proj4string(oceans))
ocean_points <- over(europe_coord, oceans)
#Add ocean points to dataset
coord$ocean <- ocean_points$featurecla
#Exlude ocean points
europe_land <- coord %>% filter(is.na(ocean))
#Load worldmap
world <- map_data("world")
#Plot europe spatial data
ggplot() + geom_map(data = world, map = world,
aes(long, lat, map_id = region), color = "white",
fill = "lightgray", size = 0.1) +
geom_point(data = europe_land,aes(long, lat),
alpha = 0.7, size = 0.05) + ylim(0,70) +
coord_sf(xlim = c(-15, 45), ylim = c(30, 75), expand = FALSE)

Plotting a matrix on ggmap with geom_tile not working

I'm trying to plot a matrix (mostly random numbers with a few NAs) with longitude/latitude coordinates on a ggmap plot.
This is my code:
library(ggmap)
library(ggplot2)
define the longitude and latitude coordinates
x1=34.2
x2=42.4
y1=37.4
y2=29.4
lon = seq(x1,x2,by=0.1)
lat = seq(y1,y2,by=-0.1)
define a matrix with random numbers with the longitude/latitude dimensions
set.seed(1)
numbers = rnorm(length(lon)*length(lat))
var = matrix(numbers,length(lon),length(lat))
add some NAs to the matrix
var[1:5,1:5] = NA
lat_min <- min(lat)-0.3
lon_min <- min(lon)-0.3
lat_max <- max(lat)+0.3
lon_max <- max(lon)+0.3
construct the ggmap
map_box <- c(left=lon_min,bottom=lat_min,
right=lon_max,top=lat_max)
total_stmap <- get_stamenmap(bbox=map_box,zoom=5,maptype="toner")
total_ggmap <- ggmap(total_stmap,extent="panel")
make a data.frame to attribute each matrix index to the geographical coordinate
lat_df <- c()
lon_df <- c()
var_df <- c()
for (i in 1:length(lon)) {
for (j in 1:length(lat)) {
lon_df <- c(lon_df,lon[i])
lat_df <- c(lat_df,lat[j])
var_df = var[i,j]
}
}
df=data.frame(Longitude=lon_df,Latitude=lat_df,Variable=var_df)
make the plot using ggmap and geom_tile
plot = total_ggmap +
geom_tile(data=df,aes(x=Longitude,y=Latitude,fill=Variable),alpha=1/2,color="black",size=0) +
geom_sf(data = df, inherit.aes = FALSE, fill = NA)
With this code I get the message:
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
...and a blank plot.
There were two problems here. The first is that all of your values in the Variable column are the same, because you are just overwriting var_df with every iteration of your loop. The line
var_df = var[i,j]
Should be
var_df = c(var_df, var[i,j])
Secondly, you should not be using geom_sf if you have a data frame of longitude, latitude and value. geom_sf is used to plot sf objects, which is not what you have.
Instead, you need only do:
plot <- total_ggmap +
geom_tile(data = df, aes(Longitude, Latitude, fill = Variable), alpha = 1/2, size = 0)
and you get:
plot

Overlay raster layer on map in ggplot2 in R?

I am trying to overlay a raster layer onto a map in ggplot. The raster layer contains likelihood surfaces for each time point from a satellite tag. I also want to set cumulative probabilities(95%, 75%, 50%) on the raster layer.
I have figured out how to show the raster layer on the ggplot map, but the coordinates are not aligned with one another. I tried making each have the same projection but it does not seem to be working... I want them both to fit the boundaries of my model (xmin = 149, xmax = 154, ymin = -14, ymax = -8.75
Attached is my r code and the figure result:
#load data
ncname <- "152724-13-GPE3"
ncfname <- paste(ncname, ".nc", sep = "")
ncin <- nc_open(ncfname)
StackedObject<-stack("152724-13-GPE3.nc", varname = "monthly_residency_distributions")
MergedObject<-overlay(StackedObject,fun=mean )
MergedObject[is.na(MergedObject)]<-0
Boundaries<-extent(c(149, 154, -14, -8.75))
ExtendedObject<-extend(MergedObject, Boundaries)
Raster.big<-raster(ncol=1200,nrow=900,ext=Boundaries)
Raster.HR<-resample(x=ExtendedObject, y=Raster.big, method="bilinear")
Raster.HR#data#values<- Raster.HR#data#values/sum(Raster.HR#data#values)
RasterVals<-sort(Raster.HR#data#values)
Raster.breaks <- c(RasterVals[max(which(cumsum(RasterVals)<= 0.05 ))], RasterVals[max(which(cumsum(RasterVals)<= 0.25 ))], RasterVals[max(which(cumsum(RasterVals)<= 0.50 ))], 1)
Raster.cols<-colorRampPalette(c("yellow","orange","red"))
RasterCols<- c(Raster.cols(3))
#Create Map
shape2 <- readOGR(dsn = "/Users/shannonmurphy/Desktop/PNG_adm/PNG_adm1.shp", layer = "PNG_adm1")
map<- crop(shape2, extent(149, 154, -14, -8.75))
projection(map)<- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
p <- ggplot() + geom_polygon(data = map, aes(x = long, y = lat, group = group), color = "black", size = 0.25) + coord_map()
projection(Raster.HR)<- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
#plot raster and ggplot
par(mfrow=c(1,1))
plot(p)
par(mfrow=c(1,1), new = TRUE)
plot(Raster.HR, col=RasterCols, breaks=Raster.breaks, legend = NULL, bbox(map))
Please let me know if there is another package/line of code I should be using to do this! Appreciate any help
Ok I understand. You want to plot multiple raster layers on the ggplot or you want that the raster object is over your background polygon object. The problem with rasterVis::gplot is that it directly plot the raster and does not allow to add another one under or over. You remind me that I already had this need and modified function gplot to retrieve the data as a tibble so that you can then play with it as much as you want with dplyr and then ggplot2. Thanks for the reminder, I added it in my current github library for later use!
Let's use a reproducible example to show this function:
Create datasets
Create a map of the world as a Raster to be use as background Raster map
Create a raster of data, here a distance from a point (limited to a maximum distance)
The code:
library(raster)
# Get world map
library(maptools)
data(wrld_simpl)
# Transform World as raster
r <- raster(wrld_simpl, res = 1)
wrld_r <- rasterize(wrld_simpl, r)
# Lets create a raster of data
pt1 <- matrix(c(100,0), ncol = 2)
dist1 <- distanceFromPoints(r, pt1)
values(dist1)[values(dist1) > 5e6] <- NA
plot(dist1)
# Plot both
plot(wrld_r, col = "grey")
plot(dist1, add = TRUE)
Function to extract (part of) raster values and transform as a tibble
#' Transform raster as data.frame to be later used with ggplot
#' Modified from rasterVis::gplot
#'
#' #param x A Raster* object
#' #param maxpixels Maximum number of pixels to use
#'
#' #details rasterVis::gplot is nice to plot a raster in a ggplot but
#' if you want to plot different rasters on the same plot, you are stuck.
#' If you want to add other information or transform your raster as a
#' category raster, you can not do it. With `SDMSelect::gplot_data`, you retrieve your
#' raster as a tibble that can be modified as wanted using `dplyr` and
#' then plot in `ggplot` using `geom_tile`.
#' If Raster has levels, they will be joined to the final tibble.
#'
#' #export
gplot_data <- function(x, maxpixels = 50000) {
x <- raster::sampleRegular(x, maxpixels, asRaster = TRUE)
coords <- raster::xyFromCell(x, seq_len(raster::ncell(x)))
## Extract values
dat <- utils::stack(as.data.frame(raster::getValues(x)))
names(dat) <- c('value', 'variable')
dat <- dplyr::as.tbl(data.frame(coords, dat))
if (!is.null(levels(x))) {
dat <- dplyr::left_join(dat, levels(x)[[1]],
by = c("value" = "ID"))
}
dat
}
Plot multiple rasters in ggplot
You can use gplot_data to transform any raster as a tibble. You are then able to add any modification using dplyr and plot on ggplot with geom_tile. The interesting point is that you can use geom_tile as many time as you want with different raster data, provided that fill option is comparable. Otherwise, you can use the trick below to remove NA values in the background raster map and use a unique fill colour.
# With gplot_data
library(ggplot2)
# Transform rasters as data frame
gplot_wrld_r <- gplot_data(wrld_r)
gplot_dist1 <- gplot_data(dist1)
# To define a unique fill colour for the world map,
# you need to remove NA values in gplot_wrld_r which
# can be done with dplyr::filter
ggplot() +
geom_tile(data = dplyr::filter(gplot_wrld_r, !is.na(value)),
aes(x = x, y = y), fill = "grey20") +
geom_tile(data = gplot_dist1,
aes(x = x, y = y, fill = value)) +
scale_fill_gradient("Distance",
low = 'yellow', high = 'blue',
na.value = NA) +
coord_quickmap()
Plot raster over polygons
Of course, with a background map as a polygon object, this trick also let you add your raster over it:
wrld_simpl_sf <- sf::st_as_sf(wrld_simpl)
ggplot() +
geom_sf(data = wrld_simpl_sf, fill = "grey20",
colour = "white", size = 0.2) +
geom_tile(data = gplot_dist1,
aes(x = x, y = y, fill = value)) +
scale_fill_gradient("Distance",
low = 'yellow', high = 'blue',
na.value = NA)
EDIT: gplot_data is now in this simple R package: https://github.com/statnmap/cartomisc

google map from dismo::gmap() and ggplot2

I obtained a map with the function dismo::gmap() and want to plot it with ggplot2 because I want to add different feautures using geom_point and other ggplot functions. I prefer to use dismo::gmap instead of ggmap::get_map() to download the google map layer. This is because dismo::gmap(), unlike ggmap::get_map(), returns a raster layer from package raster including complete CRS information and therefore is should be possible to modify the projection of the layer.
> head(data_info$latitude, 20)
#[1] 49.11306 49.39333 48.78083 51.85000 53.57361 50.67806 52.69083 52.21389 53.46361 50.99917 53.99750 53.54528 53.61417 48.00556 48.01306 53.45000
#[17] 51.93667 54.53083 51.95500 54.29639
> head(data_info$longitude, 20)
#[1] 13.134722 12.323056 13.803889 12.177778 14.143611 13.175833 12.649444 13.454167 11.629722 10.906111 11.415556 8.426944 7.160000 11.123889 10.786111
#[16] 12.766667 11.987222 13.091389 10.967500 13.684167
e = extent(-14 , 58 , 28 , 64)
mapImageData2 <- gmap(e, type = c("terrain"), lonlat = TRUE,
path = "&style=feature:all|element:labels|visibility:off&style=feature:administrative.country|element:geometry.stroke|visibility:off")
mapImageData2_proj <- projectExtent(mapImageData2, crs = "+proj=utm +zone=31 +datum=WGS84")
# plot the points on the map
ggplot(mapImageData2_proj, extent = "device") +
geom_point(inherit.aes = FALSE, aes(x = data_info$longitude, y = data_info$latitude),
data = gps, colour = "red", size = 1, pch = 20)
After trying this, I get the following error:
Error: ggplot2 doesn't know how to deal with data of class
RasterLayer
If I try this
plot(mapImageData2_proj)
Error in .plotraster2(x, col = col, maxpixels = maxpixels, add = add,
: no values associated with this RasterLayer
There are two issues in this question. One is how to get ggplot2 to plot a Raster* object. The other is how to reproject a raster while retaining its values.
The OP contains the code
library(dismo)
e = extent(-14 , 58 , 28 , 64)
mapImageData2 <- gmap(e, type = c("terrain"), lonlat = TRUE,
path = "&style=feature:all|element:labels|visibility:off&style=feature:administrative.country|element:geometry.stroke|visibility:off")
If we run this and then do plot(mapImageData2) we get a nice plot. The OP then runs
mapImageData2_proj <- projectExtent(mapImageData2, crs = "+proj=utm +zone=31 +datum=WGS84")
Now if we run plot(mapImageData2_proj) we get an error! That's because projectExtent returns a RasterLayer with no values. We need to use projectRaster() instead. See ?projectExtent for details. So we run:
mapImageData2_proj <- projectRaster(mapImageData2, crs = "+proj=utm +zone=31 +datum=WGS84")
plot(mapImageData2_proj)
Now we see the reprojected map. Progress! But we still can't plot mapImageData2_proj using ggplot2, because ggplot2 doesn't know how to handle a Raster* object. We need to convert our raster to a dataframe. There are a few ways to do this, but without loading any additional packages, a good option is raster::rasterToPoints(). For example:
myPoints <- raster::rasterToPoints(myRaster)
myDataFrame <- data.frame(myPoints)
colnames(myDataFrame) <- c("Longitude", "Latitude", "Values")
ggplot(data=myDataFrame, aes_string(y = "Latitude", x = "Longitude")) +
geom_raster(aes(fill = Values))
So to put it all together in the OP's example:
library(dismo)
e = extent(-14 , 58 , 28 , 64)
mapImageData2 <- gmap(e, type = c("terrain"), lonlat = TRUE,
path = "&style=feature:all|element:labels|visibility:off&style=feature:administrative.country|element:geometry.stroke|visibility:off")
plot(mapImageData2)
mapImageData2_proj <- projectRaster(mapImageData2, crs = "+proj=utm +zone=31 +datum=WGS84")
plot(mapImageData2_proj)
myRaster <- mapImageData2_proj
myPoints <- raster::rasterToPoints(myRaster)
myDataFrame <- data.frame(myPoints)
colnames(myDataFrame) <- c("X", "Y", "Values")
ggplot(data=myDataFrame, aes_string(y = "Y", x = "X")) +
geom_raster(aes(fill = Values))
To directly plot a Raster* object in ggplot, you can use the library RasterVis as follows:
r <- raster(system.file("external/test.grd", package="raster"))
s <- stack(r, r*2)
names(s) <- c('meuse', 'meuse x 2')
library(ggplot2)
theme_set(theme_bw())
gplot(s) + geom_tile(aes(fill = value)) +
facet_wrap(~ variable) +
scale_fill_gradient(low = 'white', high = 'blue') +
coord_equal()
As you see, we use gplot and not ggplot.
This function is for Raster* objects as it allows to load only a part of the Raster* object in RAM. You can even choose the maximum number of pixels to load on the map with gplot(s, maxpixels=50000).
Indeed, I recommend not to transform your Raster as points, because, if your raster is huge, you will not be able to load it in the RAM...

Resources