Skip to content

Instantly share code, notes, and snippets.

@ateucher
Last active June 15, 2018 16:48
Show Gist options
  • Save ateucher/1467691edbc1fd1f7fbbabd05957cbb5 to your computer and use it in GitHub Desktop.
Save ateucher/1467691edbc1fd1f7fbbabd05957cbb5 to your computer and use it in GitHub Desktop.
ggmap_sf
library(ggplot2)
library(ggmap)
library(sf)
#> Linking to GEOS 3.6.2, GDAL 2.3.0, proj.4 5.1.0

nc <- st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
nc <- st_transform(nc, 4326)

bounding <- sf::st_bbox(nc)  # get the bounding box of the data - we'll use this to get the basemap
names(bounding) <- c("left","bottom","right","top") 

map <- get_map(location = bounding, zoom = 6)

# Just plotting, the geom_sf layer is not aligned properly:
ggmap(map) + 
  geom_sf(data = nc, aes(fill = AREA), inherit.aes = FALSE)
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

devtools::source_gist("1467691edbc1fd1f7fbbabd05957cbb5", 
                      filename = "ggmap_sf.R")

# Plot with new function, add the sf object (must st_transform to 3857):
ggmap_sf(map) + 
  geom_sf(data = st_transform(nc, 3857), aes(fill = AREA), inherit.aes = FALSE)
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Created on 2018-06-15 by the reprex package (v0.2.0).

#' Plot a ggmap object when you will be adding geom_sf() layers
#'
#' This is a wrapper around [ggmap::ggmap()], that transforms the bounding
#' box of the ggmap object to epsg:3857, which is what the underlying
#' data is in. This allows you to add `geom_sf()` layers with a crs
#' of epsg:3857 and have them line up properly.
#'
#' See [this stackoverflow question](https://stackoverflow.com/q/47749078/1736291)
#'
#'
#' @param ggmap an object of class ggmap (from [ggmap::get_map()])
#' @param ... arguments passed on to [ggmap::ggmap()]
#'
#' @return ggplot2 object
#' @export
ggmap_sf <- function(ggmap, ...) {
if (!inherits(ggmap, "ggmap")) stop("map must be a ggmap object")
if (!requireNamespace("sf", quietly = TRUE)) stop("Package sf required")
# A mapping between sf bbox names and ggmap bbox names so don't mix them up
bbox_ggmap_names <- c("ymin" = "ll.lat",
"xmin" = "ll.lon",
"ymax" = "ur.lat",
"xmax" = "ur.lon")
# Extract the bounding box (in lat/lon) from the ggmap to a numeric vector,
# and set the names to what sf::st_bbox expects:
map_bbox <- setNames(unlist(attr(ggmap, "bb"))[bbox_ggmap_names],
names(bbox_ggmap_names))
# Convert the bbox to an sf polygon, transform it to 3857,
# and convert back to a bbox (convoluted, but it works)
bbox_3857 <- sf::st_bbox(
sf::st_transform(
sf::st_as_sfc(sf::st_bbox(map_bbox, crs = 4326)),
crs = 3857)
)
# Set names back to those expected in attr(map, "bb")
names(bbox_3857) <- bbox_ggmap_names[names(bbox_3857)]
# Overwrite the bbox of the ggmap object with the transformed coordinates
attr(ggmap, "bb") <- data.frame(as.list(bbox_3857))
ggmap::ggmap(ggmap, ...) +
ggplot2::coord_sf(crs = sf::st_crs(3857)) # force the ggplot2 map to be in 3857
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment