Skip to content

Instantly share code, notes, and snippets.

@aaronschiff
Created June 10, 2022 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aaronschiff/ed9334cdeae2bf43b7e5d70d1d17c42f to your computer and use it in GitHub Desktop.
Save aaronschiff/ed9334cdeae2bf43b7e5d70d1d17c42f to your computer and use it in GitHub Desktop.
Create geom_tile chart with tiles of fixed size
# The objective is to make a faceted ggplot 'heatmap' with geom_tile where the
# rendered tiles in the output PNG file have a fixed width and height,
# regardless of whatever else is in the chart.
# When ggplot charts are built, the height and width of all components are
# predetermined *except* for the facet panels. Thus the approach here is to
# render an initial version of the chart to determine the height and width
# of the predetermined elements. Then the overall height and width of the
# output PNG are calculated based on what is needed to give the rendered
# tiles the desired size.
library(tidyverse)
library(grid)
# Target tile size
target_tile_width <- 1.0
target_tile_height <- 0.5
target_tile_unit <- "cm"
# Test chart data
dat <- tibble(
y = c("A", "A", "A", "B", "B", "B", "C", "C", "C", "D", "D", "D", "E", "E", "E"),
x = rep(c(1L, 2L, 3L), 5),
outcome = c("high", "high", "low", "high", "high", "low", "low", "high", "low", "low", "low", "low", "high", "high", "low"),
area = c(rep("Lower", 9), rep("Upper", 6))
)
# Create chart
chart <- dat |>
ggplot(mapping = aes(y = y, x = x, fill = outcome)) +
geom_tile(colour = "white", size = 1) +
facet_grid(rows = vars(area), scales = "free", space = "free") +
theme_minimal() +
labs(
title = "Test grid",
x = "X axis",
y = "Y axis"
)
# Number of rows and colums of tiles in the chart
# Probably could calculate this from chart?
n_tile_rows <- 5L
n_tile_cols <- 3L
# Clear any existing viewports
popViewport(n = 0L)
# Make a temporary viewport to do our size calculations in
# I don't think the size of this matters but it needs to be in the
# same units as target_tile_unit
temp_vp <- viewport(
width = unit(x = 2 * n_tile_cols * target_tile_width, units = target_tile_unit),
height = unit(x = 2 * n_tile_rows * target_tile_height, units = target_tile_unit),
xscale = c(0, 1),
yscale = c(0, 1)
)
pushViewport(temp_vp)
# Build the chart
g <- ggplot_gtable(ggplot_build(chart))
# Calculate known widths and heights of fixed elements
# Panel elements will have 'null' units and will be excluded from these sums
known_wds <- sum(convertWidth(x = g$widths, unitTo = target_tile_unit, valueOnly = TRUE))
known_hts <- sum(convertHeight(x = g$heights, unitTo = target_tile_unit, valueOnly = TRUE))
# Calculate the required output width and height
output_width <- known_wds + n_tile_cols * target_tile_width
output_height <- known_hts + n_tile_rows * target_tile_height
# Save chart
ggsave(
plot = chart,
filename = "test.png",
device = "png",
width = output_width,
height = output_height,
units = target_tile_unit,
bg = "white"
)
# Clean up temporary viewport
popViewport()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment