google map from dismo::gmap() and ggplot2 - r

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...

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")

ggplot gridded SpatialPolygonsDataFrame producing odd triangles and subsetting data based on point data

Using the code below I can plot the following:
This code is adapted from here
As you can see there are few issues with the plot. I am struggling to
Remove weird lines in plot
Only plot cells (grids) where there are data
Plot ID (see gridSpatialPolygons$values) on top of the grid cell
I realise there are a few points to this question but I hope one solution solves all.
# Load libraries
library(sp)
library(raster)
library(ggplot2)
# Projection
wgs.84 <- CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
# Load data
x <- c(76.82973, 76.82972, 76.82969, 76.83076, 76.83075, 76.83071, 76.83129, 76.83126, 76.83125)
y <- c(28.26734, 28.26644, 28.26508, 28.26778, 28.26733, 28.26507, 28.26912, 28.26732, 28.26687)
z <- c(-56.7879, -58.22462, -58.4211, -55.75333, -58.55153, -56.38619, -56.11011, -58.17415, -59.77212)
# Create data frame
dataset <- data.frame("LONGITUDE" = x, "LATITUDE" = y, "VALUES" = z)
# Create SpatialPointsDataFrame object
datasetSP <- SpatialPointsDataFrame(coords = dataset[,c(1,2)], data = data.frame("id" = 1:nrow(dataset), "values" = dataset$VALUES), proj4string = wgs.84)
# Extent
extentDatasetSP <-extent(datasetSP)
# Make grid options
# Cell size (map units)
xCellSizeGrid <- 0.001
yCellSizeGrid <- 0.001
# Grid
grid <- GridTopology(cellcentre.offset = c(extentDatasetSP#xmin, extentDatasetSP#ymin),
cellsize = c(xCellSizeGrid, yCellSizeGrid),
cells.dim = c(3, 7))
# Create SpatialGrid object
gridSpatial <- SpatialGrid(grid = grid, proj4string = wgs.84)
# Convert to SpatialPixels object
gridSpatialPixels <- as(gridSpatial, "SpatialPixels")
# Convert to SpatialPolygons object
gridSpatialPolygons <- as(gridSpatialPixels, "SpatialPolygons")
# Add 'id' and 'values' to every polygon
gridSpatialPolygons$id <- 1:nrow(coordinates(gridSpatialPolygons))
gridSpatialPolygons$values <- paste("Gridvalue", 1:nrow(coordinates(gridSpatialPolygons)), sep = ":")
# Get attributes from polygons
samplePointsInPolygons2 <- datasetSP %over% gridSpatialPolygons
ggplot(gridSpatialPolygons, aes(x = long, y = lat)) +
geom_polygon(color = "red") +
geom_point(data = dataset,
aes(x = LONGITUDE,
y = LATITUDE))
When it comes to spatial objects, ggplot2 (and tidyverse in general) seems to play nicer with sf than sp. The advice below is taken from one of the help files in the associated broom package:
Note that the sf package now defines tidy spatial objects and is the
recommended approach to spatial data. sp tidiers are likely to be
deprecated in the near future in favor of sf::st_as_sf(). Development
of sp tidiers has halted in broom.
Things should be fairly straightforward after conversion to sf.
library(dplyr)
sf::st_as_sf(gridSpatialPolygons) %>%
filter(id %in% samplePointsInPolygons2$id) %>% # keep only grid cells with data
ggplot() +
geom_sf(colour = "red") +
geom_sf_text(aes(label = values), # label cells
nudge_y = 0.0003, colour = "grey40") +
geom_point(data = dataset,
aes(x = LONGITUDE,
y = LATITUDE))

Trouble overlaying kriged data on google map

In the code below, I am trying to overlay kriged meuse data on to google map usingggmap().The code seems to work ok all the way down toget_map(),but gets stuck in some kind of huge computation (RAM usage jumps to 7G on my 8G machine - be warned!) at the lastprint(ggmap())execution. What am I doing wrong?
UPDATE: I think I figured out the problem =>stat_contour()does not seem to work with the kriged data (still not sure why). When I replace it with the binning functionstat_summary_2d(),the code now works. I'd still like to show contour lines, if possible. Can someone help with that?
Thanks
# transform meuse data to SpatialPointsDataFrame
suppressMessages(library(sp))
data(meuse)
coordinates(meuse) <- ~ x + y
proj4string(meuse) <- CRS("+proj=stere
+lat_0=52.15616055555555 +lon_0=5.38763888888889
+k=0.999908 +x_0=155000 +y_0=463000
+ellps=bessel +units=m +no_defs
+towgs84=565.2369,50.0087,465.658,
-0.406857330322398,0.350732676542563,-1.8703473836068, 4.0812")
# define sample grid for kriging
set.seed(42)
grid <-spsample(meuse, type = "regular", n = 10000)
# do kriging
suppressMessages(library(automap))
krg <- autoKrige(formula = copper ~ 1,
input_data = meuse,
new_data = grid)
# extract kriged data
krg_df <- data.frame(krg$krige_output#coords,
pred = krg$krige_output#data$var1.pred)
names(krg_df) <- c("x", "y", "pred")
# transform to SpatialPointsDF & assign original (meuse) projection
krg_spdf <- krg_df
coordinates(krg_spdf) <- ~ x + y
proj4string(krg_spdf) <- proj4string(meuse)
# transform again to longlat coordinates (for overlaying on google map below)
krg_spdf <- spTransform(krg_spdf, CRS("+init=epsg:4326"))
krg_df <- data.frame(krg_spdf#coords, pred = krg_spdf#data$pred)
# get meuse map and overlay kriged data
suppressMessages(library(ggmap))
lon <- range(krg_df$x)
lat <- range(krg_df$y)
meuse_map <- get_map(location = c(lon = mean(lon), lat = mean(lat)),
zoom = 13)
print(ggmap(meuse_map, extent = "normal", maprange = F) +
#### old code (does not work) ####
#stat_contour(aes(x = x, y = y, z = pred, fill = ..level..),
# alpha = 0.5,
# color = "gray80",
# geom = "polygon",
# data = df) +
#### replaced with stat_summary_2d() ####
stat_summary_2d(aes(x = x, y = y, z = pred),
binwidth = c(0.0005,0.0005),
alpha = 0.5,
data = krg_df) +
scale_fill_gradient(low = "yellow",
high = "red",
name = "Copper") +
coord_cartesian(xlim = lon, ylim = lat, expand = 0) +
theme(aspect.ratio = 1))

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

fortify data from shp file and promote it to SpatialPixelsDataFrame

I am trying to fortify a large shp file to raster file for analyzing (‘Koeppen Geiger’ climatic zones) with several locations. I got an error message running the ggplot and final line of code:
Error in points2grid(points, tolerance, round) : dimension 1 :
coordinate intervals are not constant
library(raster)
library(sp)
library(maptools)
library(rgdal)
library(ggplot2)
library(rgeos)
##reading shp file [https://www.climond.org/Core/Authenticated/KoppenGeiger.aspx][1]
shape.dir <- "WC05_1975H_Koppen_Shapefile"
lon.shape <- readOGR(shape.dir, layer = "WC05_1975H_Koppen")
##fortify the data
lon.df <- fortify(lon.shape, region = "Koppen")
##Using ggplot2 and get the wrong color zones
map <- get_map(location = c(73.60226, 15.77538, 153.9856, 53.56944),
color = "color",source = "google",
maptype = "terrain")
ggmap(map) +
geom_polygon(aes(x = long,
y = lat,
fill = id),
data = lon.df) +
labs(x = "Longitude",
y = "Latitude")
## The color presented was wrong with ‘Koeppen Geiger’ climatic zones
#convert to sp SpatialPointsDataFrame
coordinates(lon.df) = c("long", "lat")
#promote to SpatialPixelsDataFrame
gridded(lon.df) <- TRUE
Perhaps do
x <- lon.shape
gridded(x) <- TRUE

Resources