As a starting point, I use the very helpful code from the answer by Kat to this question (How to add less roughness to the borders of a map than to the fill of the map) to create two graphs, with the intent that one of the graphs be laid on top of the other.
library(magrittr)
library(ggplot2)
#devtools::install_github("xvrdm/ggrough")
library(ggrough)
library(sf)
library(htmltools)
library(ggiraph)
trace(ggrough:::parse_rough, edit=TRUE)
#In the popup window, paste this so that parse_rough will use parse_sf for GeomSf.
function (svg, geom)
{
rough_els <- list()
if (geom %in% c("GeomCol", "GeomBar", "GeomTile",
"Background")) {
rough_els <- append(rough_els, parse_rects(svg))
}
if (geom %in% c("GeomArea", "GeomViolin", "GeomSmooth",
"Background")) {
rough_els <- append(rough_els, parse_areas(svg))
}
if (geom %in% c("GeomPoint", "GeomJitter", "GeomDotPlot",
"Background")) {
rough_els <- append(rough_els, parse_circles(svg))
}
if (geom %in% c("GeomLine", "GeomSmooth", "Background")) {
rough_els <- append(rough_els, parse_lines(svg))
}
if (geom %in% c("Background")) {
rough_els <- append(rough_els, parse_texts(svg))
}
if (geom %in% c("GeomSf")) {
rough_els <- append(rough_els, parse_sf(svg))
}
purrr::map(rough_els, ~purrr::list_modify(.x, geom = geom))
}
# Create the function parse_sf.
parse_sf <- function (svg) {
shape <- "path"
keys <- NULL
ggrough:::parse_shape(svg, shape, keys) %>% {
purrr::map(.,
~purrr::list_modify(.x,
points = .x$d,
shape = "path"
))
}
}
nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
b <- ggplot(nc) + geom_sf(color = "black") + theme_minimal() +
theme(panel.grid = element_line(color = NA), # not resized or removed! (keep spacing)
axis.text = element_text(color = NA))
options <- list(GeomSf = list(fill_style = "hachure", angle = 60, angle_noise = 1,
gap_noise = 0, gap = 6, fill_weight = 2, bowing = 5,
roughness = 30))
(xx <- get_rough_chart(b, options)) # from your question
fixer <- function(ggr) { # where ggr is the ggrough graph
nd <- lapply(1:length(ggr$x$data), function(j) {
if(!is.null(ggr$x$data[[j]]$lengthAdjust)) { # if a text element (axis label)
ggr$x$data[[j]]$content <- "" # remove text, but keep spacing
ggr$x$data[[j]] # return modified data element
} else {
ggr$x$data[[j]] # not text, return orig data
}
})
ggr$x$data <- nd # add mod data to graph
ggr # return mod graph
}
xx2 <- xx %>% fixer() # modify the plot, to hide text
(g2 <- ggplot(nc) +
geom_sf(fill = "transparent", color = "black", linewidth = 2) +
theme_minimal() +
theme(plot.background = element_rect(fill = NA, color = "transparent"), # no white background
panel.background = element_rect(fill = NA, color = "transparent"),
text = element_text(size = 9))) # text size to match defaults in ggrough
gg <- girafe(ggobj = g2, width_svg = 7, height_svg = 5) # h/w default w/ ggrough
browsable(div( # parent div, size matches ggrough's default
style = css(width = "960px", height = "500px", position = "relative"),
div(xx2, style = css(display = "block")), # ggrough graph
div(gg, style = css(position = "absolute", top = 0, padding.top = "54.2px", # layer behind
width = "610px", height = "500px", z.index = -2))
)) # size and padding found by trial and error with defaults for graph sizes
The graph in the answer appears to be overlaid properly. However, when I run the same code (in RStudio), the overlay I get is incorrect:
I have also tried it on RStudio on a different computer, where I also get imperfect overlay, but to a different extent.
I have two questions:
div
s near the end, but this didn't work.I could find no clear information as to how ggrough
translates to literal sizes.
In this answer I've used an aspect ratio of width x height of 7 x 5 (inches) which is pretty standard (browser
, knitr
, and the like). I did not test to see if these formulas would hold up under different aspect ratios.
Because I couldn't nail down what looks like throwing mud at a ceiling fan for ggrough
, I built hundreds of graphs, aligned them, then determined if there was a forumla I could create to accurately identify the right dimensions for the ggplot object.
When I refer to the ggplot
I mean the border layer that sits on top.
When I refer to the ggrough
I mean the fill layer that is on the bottom.
The following metrics change depending on the height and width assigned to the ggrough.
Because these elements are scattered throughout different functions, I created a UDF that relies on 3 inputs: ggrough's width & height & the data to plot.
The value you assign for width & height...I believe these represent inches, but I wouldn't assume that 1 = 1 inch. There is nothing in their documentation that identifies what these values represent, but 1 equates to about 72 pixels, which is accurate if 1 represents an inch.
There are 2 error catching calls written into the function. 1) If the height exceeds the width: which only creates more useless white space above the ggrough. 2) If the values given for height/width exceed the browser window size. (If it does the ggrough plot loses its aspect ratio and hides part of the plot - nothing good there!) If you trigger either you'll see a message in the console letting you know what happened.
I left a message I was using for validation in the code, because you may find it to be useful information. It just spits out all the calculated metrics into the console when processing the data.
aligner <- function(grw, grh, nc) { # set rough chart width/height (8, 5, for example), data used in plot
if(isTRUE(grh > grw)) {
return(cat("\033[0;37;101mThe height should not exceed the width for this plot.\033[0m\n"))
}
# hide the text in the ggrough object
fixer <- function(ggr) { # where ggr is the ggrough graph
nd <- lapply(1:length(ggr$x$data), function(j) {
if(!is.null(ggr$x$data[[j]]$lengthAdjust)) { # if a text element (axis label)
ggr$x$data[[j]]$content <- "" # remove text, but keep spacing
ggr$x$data[[j]] # return modified data element
} else {
ggr$x$data[[j]] # not text, return orig data
}
})
ggr$x$data <- nd # add mod data to graph
ggr # return mod graph
}
tpad <- function(grw, grh) { # calculate the top padding, given ggrough width, height
delta <- grh - grw + 1
widD <<- 8 - grw
widH <- 7 - grh
45.5 - widD * 10.0 + delta * 35.5 + .5
}
#---------- set the variables -----------
pltW = 7; pltH = 5 # setting aspect ratio; if this changes nothing else may work correctly!
browsW = 960 # browser width (default is 960) for htmlwidgets/ used for error checking
browsH = 500 # browser height (default is 500) for htmlwidgets/ used for error checking
sfs <- list(-0.325, 6.1) # slope formula metrics for calculating font size of ggplot object
sdw <- list(72.5, -3) # slope formula metrics for calculating width of div
#---------- calculate metrics -----------
fs <- sfs[[1]] * grw + sfs[[2]] # calculate the approriate font size for ggplot object
dw <- sdw[[1]] * grw + sdw[[2]] # calculate the approriate div width for ggplot object
if(any(pltH/pltW * dw > browsH)) { # validate aspect ratio by width fits in browser window
return(cat("\033[0;37;101mWidth of", grw, "is too high to fit. Reduce width & try again.\033[0m\n"))
}
tp <- tpad(grw, grh) # calculate top padding
if(isTRUE(tp < 0)) { # validate aspect ratio fits in browser window
return(cat("\033[0;37;101mWith the given height and width, the plot doesn't fit. Try increasing the height.\033[0m\n"))
}
lp <- ifelse(isTRUE(widD > 0), 1 - .5 * widD, 1) # calculate the left padding
#------------ create graphs ------------
b <- ggplot(nc) + geom_sf(color = "black") + theme_minimal() +
theme(panel.grid = element_line(color = NA), # not resized or removed! (keep spacing)
axis.text = element_text(color = NA))
options <- list(GeomSf = list(fill_style = "hachure", angle = 60, angle_noise = 1,
gap_noise = 0, gap = 6, fill_weight = 2, bowing = 5,
roughness = 30))
xx <- get_rough_chart(b, options, width = grw, height = grh) %>% fixer() # create gg rough graph
g2 <- ggplot(nc) +
geom_sf(fill = "transparent", color = "black", linewidth = 2) +
theme_minimal() +
theme(plot.background = element_rect(fill = NA, color = "transparent"), # no white background
panel.background = element_rect(fill = NA, color = "transparent"),
text = element_text(size = rel(fs))) # text size to match defaults in ggrough
gg <- girafe(ggobj = g2, width_svg = pltW, height_svg = pltH) # great ggplot graph HTML
#-------- notify user of calcs ---------
message(paste0("Entered size: ", grw, ", ", grh, "; calculated dims are: ",
"div width ", dw, "; font size ", fs, "; top padding ", tp,
" and left padding ", lp))
#-------- create graph overlay ---------
browsable(div( # parent div, size matches ggrough's default
style = css(width = "960px", height = paste0(browsH, "pt"), position = "relative"),
div(xx, style = css(display = "block", padding.left = paste0(lp, "px"))), # ggrough graph
div(gg, style = css(position = "absolute", top = 0, # ggplot graph
padding.top = paste0(tp, "px"),
width = paste0(dw, "px"), z.index = -2))
))
}
aligner(7, 7, nc)
While SO naturally maxes the image size to fit, you can at least see the variation in the font sizes.
Or at 5, 5:
Or at 9, 7:
This is an example of the output dims and an error message that you may see.