I have a map of a region of canada that I created using ggplot and the sf package. At specific points on the map, I'd like to add a line leading to a callout of a graph using data from that location. I know how to do popups in leaflet, but this needs to be a document that works when I print out a hard copy. I also could do it in Illustrator, but coding it in would be best!
Any ideas?
Here is some example code
library(ggmap)
library(sf)
library(tidyverse)
# example point object
point.forMap = data.frame(lon=-111.19, lat=56.55, ID="1A3") %>%
st_as_sf(coords=c("lon", "lat")) %>%
st_set_crs(4326)
# Example data that I want to add as a graph
data.forGraph = data.frame(ID=rep("1A3", 30), xval=runif(30, min=5, max=20), yval=runif(30, min=0, max=20) )
# data for the background of the map
background_map = get_map(location=c(lon=-111.19, lat=56.5), zoom=8,maptype="satellite")
# plot the map
ggmap(background_map)
geom_sf(data=point.forMap, inherit.aes = FALSE, size=8, color="red")
# plot the graph
ggplot(data=data.forGraph)
geom_point(aes(x=xval, y=yval))
Now the question is, how do I get the graph to appear on the map somewhere near the point? Both the data.forGraph and point.forMap have an ID column that we can join the two dataframes on, but I'm not sure where to go from here.
CodePudding user response:
One option to add your charts to your map would be via annotation_custom. In my opinion the tricky part is to get the positions right which probably requires some fiddling to get a nice plot.
But basically you could
- Put the coordinates of your points, the coordinates for the bounding boxes of the charts and the charts themselves into a
tibble. - Use a helper function to add the
annotation_customlayers for one chart which includes the chart itself as well as segment connecting the charts to the points. - Use
purrr::pmapto loop over the rows of thetibblewhich we set up in step 1 to add the charts to the map
Note: I added a second point to illustrate the general approach to add multiple charts.
library(ggmap)
library(sf)
library(ggplot2)
library(dplyr)
library(purrr)
point_df <- data.frame(lon = c(-111.19, -110),
lat = c(56.55, 56),
ID = c("1A3", "1A4"))
point.forMap <- point_df %>%
st_as_sf(coords = c("lon", "lat")) %>%
st_set_crs(4326)
# Example data that I want to add as a graph
data.forGraph <- data.frame(ID = rep("1A3", 30), xval = runif(30, min = 5, max = 20), yval = runif(30, min = 0, max = 20))
# data for the background of the map
background_map <- get_map(location = c(lon = -111.19, lat = 56.5), zoom = 8, maptype = "satellite")
# plot the map
p1 <- ggmap(background_map)
geom_sf(data = point.forMap, inherit.aes = FALSE, size = 8, color = "red")
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# Set up a tibble containing coordinates of points, bounding box of the charts and the charts
p2 <- tibble(
ID = c("1A3", "1A4"),
p = list(ggplot(data = data.forGraph) geom_point(aes(x = xval, y = yval)),
ggplot(data = data.forGraph) geom_point(aes(x = xval, y = yval)))
)
p2 <- left_join(p2, point_df, by = "ID") %>%
rename(x = lon, y = lat) %>%
mutate(xmin = x - 1, xmax = x - .2,
ymin = y .25, ymax = y .75) %>%
select(-ID)
# Helper function
add_chart <- function(p, x, y, xmin, xmax, ymin, ymax) {
list(
# Add chart
annotation_custom(
grob = ggplotGrob(p),
xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax
),
# Add segment
annotation_custom(
grob = grid::segmentsGrob(
y0 = unit(1, "npc"), y1 = unit(0, "npc"),
gp = grid::gpar(lwd = unit(1, "lines"), col = "white")
),
xmin = x, xmax = xmax, ymin = y, ymax = ymin
)
)
}
# Add charts to the map
p1
purrr::pmap(p2, add_chart)

