Following these questions and answers (How to shade shapes) and (Unable to replicate this ggplot2 plot), say that I get ggrough
(https://xvrdm.github.io/ggrough/index.html) up and running:
#install.packages("devtools") # if you have not installed "devtools" package
#devtools::install_github("xvrdm/ggrough")
library(magrittr)
library(ggplot2)
library(ggrough)
library(sf)
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"
))
}
}
I then plot a county map:
nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
b <- ggplot(nc) +
geom_sf(aes(fill = AREA))
options <- list(GeomSf=list(fill_style="hachure",
angle=60,
angle_noise=1,
gap_noise=0,
gap=6,
fill_weight=2,
bowing=5,
roughness=30))
get_rough_chart(b, options)
This produces:
If I want to 1) keep the roughness
setting (of the fill) at 30 and 2) I want to display the borders (and not remove them using the lwd=0
option in geom_sf
or something similar), how can I independently control the roughness of the borders? As is, the borders are out of control. I want them to have some amount of roughness so that it looks like they were hand drawn, but only a little. (If this is not possible, how can I include a border that is not affected by ggrough
, while the fill is still affected?)
I tried the following:
b <- ggplot(nc) +
geom_sf(aes(fill = AREA), lwd=0) +
geom_sf(fill = "transparent", color = "yellow", size = 1)
options <- list(GeomSf=list(fill_style="hachure",
angle=60,
angle_noise=1,
gap_noise=0,
gap=6,
fill_weight=2,
bowing=5,
roughness=30),
GeomSf=list(fill_style="hachure",
angle=60,
angle_noise=1,
gap_noise=0,
gap=6,
fill_weight=2,
bowing=5,
roughness=1))
get_rough_chart(b, options)
My hope was that if I included two GeomSf
elements in the list of options, maybe the first would correspond to the first geom_sf
(with no borders) and the second—--with roughness
set at 1---would correspond to the second geom_sf
. But this didn't work:
This method uses two seperate graphs, one for application with ggrough
and your customization, one that doesn't use ggrough
but introduces a traditional border.
This requires 2 additional libraries. One turns ggplot2
into html. The other is used to overlay the two plots.
library(ggiraph)
library(htmltools)
Since you've a few different plots in your question, here is the exact plotting code I used. I added theme constraints. This sets the panels and text to invisible -- but doesn't remove them. This they need to be there so that the plots can be aligned.
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))
The options were not changed. I will state that I used the first variation (with only one GeomSf
).
After you get_rough_chart
, I've got a UDF here that modifies the panel and text fields. It appears that black is used by default and it adds the axis text back into the plot. This removes the text without compromising the spacing.
(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
Next, create the border only plot. In this plot, the theme is used to remove the white background and set the font size so it matches the defaults in ggrough
.
(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
Last but not least, assembly.
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
While the above code does not have fill
assigned in geom_sf
, if I change that and add fill = "blue"
, for example, you still have the same results.
(easier copy + paste)
# uncommented code is likely unchanged code from your question
library(magrittr)
library(ggplot2)
library(ggrough)
library(sf)
library(htmltools) # <- I'm new!
library(ggiraph) # <- I'm new!
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