How to outline a group of facets using geofacet and ggplot2?

Using ggplot2 with geofacet, I want to outline some group of facets with rectangles. I guess I could draw it after the image is saved on disc as .png or .jpg with the magick package, but I would prefer to have a more programmatically approach, using only ggplot2 (if anything change the rectangle-ish is still well positioned).

Bellow is some code from the package´s vignette.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>library("ggplot2")
library("geofacet")
ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
geom_col() +
facet_geo(~ code, grid = "aus_grid1") +
coord_flip() +
labs(
title = "Australian Population Breakdown",
caption = "Data Source: ABS Labour Force Survey, 12 month average",
y = "Population [Millions]") +
theme_bw()
</code>
<code>library("ggplot2") library("geofacet") ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) + geom_col() + facet_geo(~ code, grid = "aus_grid1") + coord_flip() + labs( title = "Australian Population Breakdown", caption = "Data Source: ABS Labour Force Survey, 12 month average", y = "Population [Millions]") + theme_bw() </code>
library("ggplot2")
library("geofacet")

ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
  geom_col() +
  facet_geo(~ code, grid = "aus_grid1") +
  coord_flip() +
  labs(
    title = "Australian Population Breakdown",
    caption = "Data Source: ABS Labour Force Survey, 12 month average",
    y = "Population [Millions]") +
  theme_bw()

And the desired result is as follows (but prettier):

Any suggestion is welcome! A version with a good group backgroud outside the plot area is also acceptable.

Recognized by R Language Collective

3

You could do this by using the libraries grid and gtable.

First, make the plot and convert it to a gtable. Then use gtable_show_layout() to display the grid visually and get row/column numbers:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>library("ggplot2")
library("geofacet")
library("gtable")
library("grid")
# Make the plot
plt <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
geom_col() +
facet_geo(~ code, grid = "aus_grid1") +
coord_flip() +
labs(
title = "Australian Population Breakdown",
caption = "Data Source: ABS Labour Force Survey, 12 month average",
y = "Population [Millions]") +
theme_bw()
# Turn it into a gtable
gtab <- get_geofacet_grob(plt) # Normally you'd use ggplotGrob(plt), but geofacet is different
# See the gtable layout visually
gtable_show_layout(gtab)
</code>
<code>library("ggplot2") library("geofacet") library("gtable") library("grid") # Make the plot plt <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) + geom_col() + facet_geo(~ code, grid = "aus_grid1") + coord_flip() + labs( title = "Australian Population Breakdown", caption = "Data Source: ABS Labour Force Survey, 12 month average", y = "Population [Millions]") + theme_bw() # Turn it into a gtable gtab <- get_geofacet_grob(plt) # Normally you'd use ggplotGrob(plt), but geofacet is different # See the gtable layout visually gtable_show_layout(gtab) </code>
library("ggplot2")
library("geofacet")
library("gtable")
library("grid")

# Make the plot
plt <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
  geom_col() +
  facet_geo(~ code, grid = "aus_grid1") +
  coord_flip() +
  labs(
    title = "Australian Population Breakdown",
    caption = "Data Source: ABS Labour Force Survey, 12 month average",
    y = "Population [Millions]") +
  theme_bw()

# Turn it into a gtable
gtab <- get_geofacet_grob(plt) # Normally you'd use ggplotGrob(plt), but geofacet is different

# See the gtable layout visually
gtable_show_layout(gtab)

Next, make the purple rectangle for TAS:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',alpha=0.25))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16))
</code>
<code>purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',alpha=0.25)) grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16)) </code>
purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',alpha=0.25))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16))

But you probably want it to go a bit beyond the plot area for a background or a border, so you can take the coordinates out of the (0,1) range and use clip='off' with gtable_add_grob():

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill='purple',alpha=0.25))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off'))
</code>
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill='purple',alpha=0.25)) grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')) </code>
purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill='purple',alpha=0.25))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off'))

Still using TAS to demonstrate, you could also make it an outline rather than a shaded area. I’ve still kept the alpha (opacity) at 0.5, so when it overlaps something like a plot axis tick you can still see what’s underneath it:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=3))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off'))
</code>
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=3)) grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')) </code>
purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=3))
grid.draw(gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off'))

And here’s the whole plot done that way:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5))
bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5))
orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5))
greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5))
step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')
step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off')
step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off')
step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off')
grid.draw(step4)
</code>
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5)) bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5)) orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5)) greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5)) step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off') step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off') step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off') step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off') grid.draw(step4) </code>
purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5))
bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5))
orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5))
greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5))

step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')
step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off')
step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off')
step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off')
grid.draw(step4)

You can also set alpha to 0.25 for more transparency and fill in the shapes (I also changed the shade of blue in this one to stop it from looking purple when translucent):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5))
bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5))
orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5))
greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5))
step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')
step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off')
step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off')
step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off')
grid.draw(step4)
</code>
<code>purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5)) bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5)) orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5)) greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5)) step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off') step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off') step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off') step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off') grid.draw(step4) </code>
purplerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.02,1.02,1.02,-0.02),name='purplerect',gp=gpar(fill=NA,col='purple',alpha=0.5,lwd=5))
bluerect <- polygonGrob(x=c(-0.01,-0.01,1.01,1.01), y=c(-0.02,1.02,1.02,-0.02),name='bluerect',gp=gpar(fill=NA,col='blue',alpha=0.5,lwd=5))
orangerect <- polygonGrob(x=c(-0.02,-0.02,1.02,1.02), y=c(-0.01,1.01,1.01,-0.01),name='orangerect',gp=gpar(fill=NA,col='orange',alpha=0.5,lwd=5))
greenshape <- polygonGrob(x=c(0.5,0.5,1.01,1.01,-0.01,-0.01,0.5),y=c(0.5,1.01,1.01,-0.01,-0.01,0.5,0.5),name='greenshape',gp=gpar(fill=NA,col='green3',alpha=0.5,lwd=5))

step1 <- gtable_add_grob(gtab,purplerect,t=28,l=15,b=30,r=16,clip='off')
step2 <- gtable_add_grob(step1,bluerect,t=18,b=21,l=14,r=21,clip='off')
step3 <- gtable_add_grob(step2,orangerect,t=6,b=15,l=14,r=15,clip='off')
step4 <- gtable_add_grob(step3,greenshape,t=6,b=15,l=7,r=11,clip='off')
grid.draw(step4)

Finally, you can also use the parameter z of gtable_add_grob() to control the order things are drawn in. Here’s output of head(gtab$layout) showing that the background has z=0 and the panels have z=1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> t l b r z clip name
133 1 1 37 25 0 on background
2 15 7 15 7 1 on panel-2-1
6 10 11 10 11 1 on panel-2-2
7 15 11 15 11 1 on panel-3-2
</code>
<code> t l b r z clip name 133 1 1 37 25 0 on background 2 15 7 15 7 1 on panel-2-1 6 10 11 10 11 1 on panel-2-2 7 15 11 15 11 1 on panel-3-2 </code>
     t  l  b  r z clip       name
133  1  1 37 25 0   on background
2   15  7 15  7 1   on  panel-2-1
6   10 11 10 11 1   on  panel-2-2
7   15 11 15 11 1   on  panel-3-2

So if we draw the shapes with z=0.5, they will be drawn after the background and before the panels, and the panels will end up on top of them. This means we can shade areas of the plot besides the panel (because they are transparent/have no background besides the overall plot background) by just filling in those grid squares:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',col=NA,alpha=0.25))
bluerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='bluerect',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
orangerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='orangerect',gp=gpar(fill='orange',col=NA,alpha=0.25))
greenrect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='greenrect',gp=gpar(fill='green3',col=NA,alpha=0.25))
# For ones with y-axes, make additional rectangles that will only fill the right half of those squares
halfpurple <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfpurple',gp=gpar(fill='purple',col=NA,alpha=0.25))
halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
halfgreen <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfgreen',gp=gpar(fill='green3',col=NA,alpha=0.25))
# Add a row between the title and the top of the plots, since the title has a white background that would cover the top of the green/orange rectangles
step0 <- gtable_add_rows(gtab,unit(5.5,'pt'),pos=3)
# Shade different squares of the grid
step1 <- gtable_add_grob(step0,purplerect,t=28,b=32,l=12,r=17,z=0.5)
step2 <- gtable_add_grob(step1,halfpurple,t=28,b=32,l=11,r=11,z=0.5)
step3 <- gtable_add_grob(step2,bluerect,t=17,b=24,l=12,r=22,z=0.5)
step4 <- gtable_add_grob(step3,halfblue,t=17,b=24,l=10,r=11,z=0.5)
step5 <- gtable_add_grob(step4,orangerect,t=4,b=19,l=12,r=17,z=0.5)
step6 <- gtable_add_grob(step5,greenrect,t=4,b=18,l=8,r=13,z=0.5)
step7 <- gtable_add_grob(step6,halfgreen,t=4,b=12,l=7,r=7,z=0.5)
step8 <- gtable_add_grob(step7,greenrect,t=13,b=18,l=6,r=7,z=0.5)
grid.draw(step8)
</code>
<code>purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',col=NA,alpha=0.25)) bluerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='bluerect',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25)) halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25)) orangerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='orangerect',gp=gpar(fill='orange',col=NA,alpha=0.25)) greenrect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='greenrect',gp=gpar(fill='green3',col=NA,alpha=0.25)) # For ones with y-axes, make additional rectangles that will only fill the right half of those squares halfpurple <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfpurple',gp=gpar(fill='purple',col=NA,alpha=0.25)) halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25)) halfgreen <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfgreen',gp=gpar(fill='green3',col=NA,alpha=0.25)) # Add a row between the title and the top of the plots, since the title has a white background that would cover the top of the green/orange rectangles step0 <- gtable_add_rows(gtab,unit(5.5,'pt'),pos=3) # Shade different squares of the grid step1 <- gtable_add_grob(step0,purplerect,t=28,b=32,l=12,r=17,z=0.5) step2 <- gtable_add_grob(step1,halfpurple,t=28,b=32,l=11,r=11,z=0.5) step3 <- gtable_add_grob(step2,bluerect,t=17,b=24,l=12,r=22,z=0.5) step4 <- gtable_add_grob(step3,halfblue,t=17,b=24,l=10,r=11,z=0.5) step5 <- gtable_add_grob(step4,orangerect,t=4,b=19,l=12,r=17,z=0.5) step6 <- gtable_add_grob(step5,greenrect,t=4,b=18,l=8,r=13,z=0.5) step7 <- gtable_add_grob(step6,halfgreen,t=4,b=12,l=7,r=7,z=0.5) step8 <- gtable_add_grob(step7,greenrect,t=13,b=18,l=6,r=7,z=0.5) grid.draw(step8) </code>
purplerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='purplerect',gp=gpar(fill='purple',col=NA,alpha=0.25))
bluerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='bluerect',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
orangerect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='orangerect',gp=gpar(fill='orange',col=NA,alpha=0.25))
greenrect <- polygonGrob(x=c(0,0,1,1), y=c(0,1,1,0),name='greenrect',gp=gpar(fill='green3',col=NA,alpha=0.25))

# For ones with y-axes, make additional rectangles that will only fill the right half of those squares
halfpurple <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfpurple',gp=gpar(fill='purple',col=NA,alpha=0.25))
halfblue <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfblue',gp=gpar(fill='deepskyblue2',col=NA,alpha=0.25))
halfgreen <- polygonGrob(x=c(0.5,0.5,1,1), y=c(0,1,1,0),name='halfgreen',gp=gpar(fill='green3',col=NA,alpha=0.25))


# Add a row between the title and the top of the plots, since the title has a white background that would cover the top of the green/orange rectangles
step0 <- gtable_add_rows(gtab,unit(5.5,'pt'),pos=3)

# Shade different squares of the grid
step1 <- gtable_add_grob(step0,purplerect,t=28,b=32,l=12,r=17,z=0.5)
step2 <- gtable_add_grob(step1,halfpurple,t=28,b=32,l=11,r=11,z=0.5)
step3 <- gtable_add_grob(step2,bluerect,t=17,b=24,l=12,r=22,z=0.5)
step4 <- gtable_add_grob(step3,halfblue,t=17,b=24,l=10,r=11,z=0.5)
step5 <- gtable_add_grob(step4,orangerect,t=4,b=19,l=12,r=17,z=0.5)
step6 <- gtable_add_grob(step5,greenrect,t=4,b=18,l=8,r=13,z=0.5)
step7 <- gtable_add_grob(step6,halfgreen,t=4,b=12,l=7,r=7,z=0.5)
step8 <- gtable_add_grob(step7,greenrect,t=13,b=18,l=6,r=7,z=0.5)
grid.draw(step8)

As a final note, make sure you’re using dev.off() before you start each new plot. Otherwise it will just keep drawing the plots on top of each-other. You might not see it because the background is opaque, but drawing plots will get slower and slower if you’re troubleshooting and making lots of test versions.

2

An approach with {grid}:

  • generate sample plot p:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
library("ggplot2")
library("geofacet")
library(grid)
p <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
geom_col() +
facet_geo(~ code, grid = "aus_grid1") +
coord_flip() +
labs(
title = "Australian Population Breakdown",
caption = "Data Source: ABS Labour Force Survey, 12 month average",
y = "Population [Millions]") +
theme_bw()
</code>
<code> library("ggplot2") library("geofacet") library(grid) p <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) + geom_col() + facet_geo(~ code, grid = "aus_grid1") + coord_flip() + labs( title = "Australian Population Breakdown", caption = "Data Source: ABS Labour Force Survey, 12 month average", y = "Population [Millions]") + theme_bw() </code>

    library("ggplot2")
    library("geofacet")
    library(grid)
    
    p <- ggplot(aus_pop, aes(age_group, pop / 1e6, fill = age_group)) +
      geom_col() +
      facet_geo(~ code, grid = "aus_grid1") +
      coord_flip() +
      labs(
        title = "Australian Population Breakdown",
        caption = "Data Source: ABS Labour Force Survey, 12 month average",
        y = "Population [Millions]") +
      theme_bw()

  • this function takes the grob components of p, scans which strip grobs contain the labels of the desired states, extracts these rows from the tabular representation (gtable) of p, together with their column and row indices in the overall layout of p. The overall layout is used (pushViewport) to place a grid.rectangle around the row-/column range occupied by the desired state panels:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
draw_frame <- (x, states, ...){
gpars <- list(...)
the_grobs <- ggplotGrob(x)
widths <- the_grobs$widths
heights <- the_grobs$heights
## adopt column/row-layout from p:
vp_top <- viewport(layout = grid.layout(
nrow = length(heights),
ncol = length(widths),
heights = heights,
widths = widths
))
pushViewport(vp_top)
## find rows defining the strips (labelling) in the gtable representation of p:
z <- Map(states, f = (label) {
Map(the_grobs$grobs, f = (g) length(rapply(g, f = (x) grep(label, x))) > 0) |> unlist() |> which()
}) |> unlist()
strip_defs <- the_grobs$layout[z,]
## set viewport (drawing area) to the row- and column range occupied
## by the desired states:
pushViewport(viewport(layout.pos.col = range(strip_defs$l) + c(0, 0), # l for left
layout.pos.row = range(strip_defs$t) + c(0, 1) # t for top
))
## draw surrounding rectangle:
grid.rect(gp = gpar(z = 0, lwd = 3, col = gpars$col, fill = fill_alpha('white', 0)))
upViewport() ## zoom out to total plot area again
}
</code>
<code> draw_frame <- (x, states, ...){ gpars <- list(...) the_grobs <- ggplotGrob(x) widths <- the_grobs$widths heights <- the_grobs$heights ## adopt column/row-layout from p: vp_top <- viewport(layout = grid.layout( nrow = length(heights), ncol = length(widths), heights = heights, widths = widths )) pushViewport(vp_top) ## find rows defining the strips (labelling) in the gtable representation of p: z <- Map(states, f = (label) { Map(the_grobs$grobs, f = (g) length(rapply(g, f = (x) grep(label, x))) > 0) |> unlist() |> which() }) |> unlist() strip_defs <- the_grobs$layout[z,] ## set viewport (drawing area) to the row- and column range occupied ## by the desired states: pushViewport(viewport(layout.pos.col = range(strip_defs$l) + c(0, 0), # l for left layout.pos.row = range(strip_defs$t) + c(0, 1) # t for top )) ## draw surrounding rectangle: grid.rect(gp = gpar(z = 0, lwd = 3, col = gpars$col, fill = fill_alpha('white', 0))) upViewport() ## zoom out to total plot area again } </code>

    draw_frame <- (x, states, ...){
      gpars <- list(...)
      the_grobs <- ggplotGrob(x)
      widths <- the_grobs$widths
      heights <- the_grobs$heights
      
      ## adopt column/row-layout from p:
      vp_top <- viewport(layout = grid.layout(
        nrow = length(heights),
        ncol = length(widths),
        heights = heights,
        widths = widths
      ))
      
      pushViewport(vp_top)
      ## find rows defining the strips (labelling) in the gtable representation of p:
      z <- Map(states, f = (label) {
        Map(the_grobs$grobs, f = (g) length(rapply(g, f = (x) grep(label, x))) > 0) |> unlist() |> which()
      }) |> unlist()
      strip_defs <- the_grobs$layout[z,]
      ## set viewport (drawing area) to the row- and column range occupied
      ## by the desired states:
      pushViewport(viewport(layout.pos.col = range(strip_defs$l) + c(0, 0), # l for left
                            layout.pos.row = range(strip_defs$t) + c(0, 1) # t for top
      ))
      ## draw surrounding rectangle:
      grid.rect(gp = gpar(z = 0, lwd = 3, col = gpars$col, fill = fill_alpha('white', 0)))
      upViewport() ## zoom out to total plot area again
    }

example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
p
draw_frame(p, states = c('QLD', 'NSW'), col = 'orange')
draw_frame(p, states = c('VIC', 'ACT'), col = 'blue')
draw_frame(p, states = c('TAS'), col = 'violet')
draw_frame(p, states = c('NT', 'WA', 'SA'), col = 'green')
</code>
<code> p draw_frame(p, states = c('QLD', 'NSW'), col = 'orange') draw_frame(p, states = c('VIC', 'ACT'), col = 'blue') draw_frame(p, states = c('TAS'), col = 'violet') draw_frame(p, states = c('NT', 'WA', 'SA'), col = 'green') </code>

    p
    draw_frame(p, states = c('QLD', 'NSW'), col = 'orange')
    draw_frame(p, states = c('VIC', 'ACT'), col = 'blue')
    draw_frame(p, states = c('TAS'), col = 'violet')
    draw_frame(p, states = c('NT', 'WA', 'SA'), col = 'green')

There’s still an issue with non rectangular ranges (see NT-WA-SA) which is probably better solved with background colors.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật