Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Align points with background png in ggplot2

Tags:

r

ggplot2

png

grid

I am trying to crease a grid of points corresponding to luminescence values in a 384-well plate experiment. I am drawing the plate as a .png file and overlaying the grid such that each point should sit in one well of the plate. Example code and data provided.

Is it possible to do this with ggplot2?

I am using the following code (example data provided):

library(ggplot2)
library(png)
library(RCurl)
library(grid)

example.gg <- read.csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vRcX5aMZGCp9Bs3BRZSg8k4o-kbSjOO5z3LsRxgIv4qJHz1fG-Argruje32OuZ2Tt2qPaNGksGr4Jia/pub?output=csv",
            row.names = 1)
example.gg$Row <- factor(example.gg$Row, levels = rev(sort(unique(example.gg$Row))))

png.img <- readPNG(getURLContent("https://i.imgur.com/QeSO7d3.png"))
img.rg <- rasterGrob(png.img, interpolate=TRUE)

gp <- ggplot(example.gg,
             aes(x = Col, y = Row, col = Lum)) +
  annotation_custom(img.rg, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_point(shape = 15) + 
  theme_void()
gp

And this is the image that gets made:

enter image description here

Answer

Thanks to Dan Adams for original answer.

gp <- ggplot(example.gg,
             aes(x = Col, y = Row, col = Lum)) +
  annotation_custom(img.rg, 
                    xmin = -2,
                    xmax = 27,
                    ymin = -1,
                    ymax = 18) +
  geom_point(shape = 15, size = 2.5) + 
  theme_void()
gp + coord_fixed(clip = "off") +
  theme(plot.margin = unit(c(3, 6, 5, 2), "lines"),
        legend.position = c(1.2, 0.5)) +
  scale_colour_gradientn(colours = pals::ocean.haline(100))

enter image description here

like image 275
Yuriy Grabovsky Avatar asked Oct 25 '25 21:10

Yuriy Grabovsky


2 Answers

By manually adjusting the position of the image with xmin/xmax & ymin/ymax, fixing the pitch of rows and columns with coord_fixed(clip = "off) and expanding the plot.margin in theme I was able to get something that seems like it will work.

library(ggplot2)
library(png)
library(RCurl)
library(grid)

example.gg <- read.csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vRcX5aMZGCp9Bs3BRZSg8k4o-kbSjOO5z3LsRxgIv4qJHz1fG-Argruje32OuZ2Tt2qPaNGksGr4Jia/pub?output=csv",
            row.names = 1)
example.gg$Row <- factor(example.gg$Row, levels = sort(unique(example.gg$Row)))

png.img <- readPNG(getURLContent("https://i.imgur.com/QeSO7d3.png"))
img.rg <- rasterGrob(png.img, interpolate=TRUE)

gp <- ggplot(example.gg,
       aes(x = Col, y = Row, col = Lum)) +
  annotation_custom(
    img.rg,
    xmin = -2,
    xmax = 27,
    ymin = -1,
    ymax = 18
  ) +
  geom_point(shape = 15) +
  coord_fixed(clip = "off") +
  theme_void() +
  theme(plot.margin = unit(c(3, 2, 5, 2), "lines"))
  
gp

Created on 2022-02-14 by the reprex package (v2.0.1)

like image 195
Dan Adams Avatar answered Oct 28 '25 12:10

Dan Adams


I love a challenge, so here's the whole thing as a single ggplot call, without relying on any png files

ggplot(example.gg, aes(factor(Col, levels = 1:24), Row, fill = Lum)) + 
  geom_tile(size = 6, color = "white") + 
  geom_label(label = "  ", fill = NA, label.padding = unit(0.3, "lines")) +
  annotation_custom(roundrectGrob(gp = gpar(fill = NA)), xmin = -2, 
                    xmax = 26, ymin = -1, ymax = 19) +
  annotation_custom(roundrectGrob(gp = gpar(fill = NA), r = unit(0.01, "npc")), 
                    xmin = 0.5, xmax = 24.5, ymin = 0.5, ymax = 16.5) +
  annotation_custom(polygonGrob(
    x = c(0.05, 0.99, 1, 1, 0.99, 0.05, 0, 0, 0.05), 
    y = c(0, 0, 0.01, 0.99, 1, 1, 0.9, 0.1, 0), gp = gpar(fill = NA)),
    xmin = -1.5, xmax = 25, ymin = 0, ymax = 18) +
  scale_fill_gradientn(colours = pals::ocean.haline(100)) +
  theme_void() + 
  coord_equal(clip = "off") +
  scale_x_discrete(expand = c(0.03, 0)) +
  guides(x = guide_axis(position = "top")) +
  theme(axis.text = element_text(size = 18),
        axis.text.x.top = element_text(size = 12),
        plot.margin = margin(20, 20, 20, 40),
        legend.box.margin =  margin(20, 20, 20, 40))

enter image description here

like image 25
Allan Cameron Avatar answered Oct 28 '25 10:10

Allan Cameron