Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to fit the plot over a background image in R and ggplot2

I am trying to fit the plot that is displayed over the background image. I cannot stretch the image too much because of the loss of sharpness. Therefore, I have to fit the plot over the background image which is much thinner than the plot. I do not know how to do it. Please see the attached plot:

Example plot

Here is the sample R Code:

library(ggplot2)
library(readxl)
library(jpeg)
library(grid)

# find the ".jpg" image files and parse them to get the depth ranges for each image
setwd('~/R/Image_Example')
image_file <- "C1_9195-9197.jpg"
# read in the image file
img <- readJPEG(image_file)
g <- rasterGrob(img, interpolate = FALSE)

# read in the CT data
df_ct <- read_excel("CT_Doris25_short.xlsx", col_names = FALSE)

g_ct <- ggplot(data=df_ct) +
  annotation_custom(g, xmin=min(df_ct$X1), xmax=max(df_ct$X1), ymin=-Inf, ymax=Inf) +
  geom_path(aes_string(x=df_ct$X1, y=df_ct$X0), color='cyan') +
  scale_y_reverse() +
  theme(plot.margin = unit(c(0,0,0,0), "lines"))

g_ct

As you can see,

  1. I am trying to adjust the xmin and xmax in annotation_custom. Is there a way where the plot shown in 'cyan' color can be fit perfectly over the 'width' of the background image? I do not mind if the graph is thinner and the X-axis is not shown. In fact, that is what I do when I assemble the plot with a number of other plots using gridExtra package.

  2. I would like to display the starting depth 9195 at the top of the image shown, and ending depth 9197 at the bottom of the image. Then I do not need the Y axis. I know how to suppress the the display of X axis and Y axis labels. So that is not a problem. I turned them on for clarification. Can that be done programmatically?

  3. As you can see that the ticks along the Y axis vary in steps of 0.5, i.e., 9195, 9195.5, 9196, ... and so on. I would like to make them go up (or down) in steps of 0.1 instead of 0.5. Also, I do not want to show the Y-axis labels, just ticks. The depth at the top of the image (9195) and the bottom (9197) is sufficient.

So it should look similar to the image shown below:

It should look like this...

Please disregard the colorful plot to the left, that is a separate plot which I cannot get rid of, otherwise I cannot show the 'ticks'. It was done using MatLab. I am trying to replace the MatLab plot with an R plot. Matlab is using TIFF images which are much heavier but also much sharper. I am going with JPEG which is acceptable.

Thanks for your time.

like image 832
Bharat Avatar asked Feb 19 '17 21:02

Bharat


People also ask

How do I add an image to a ggplot2 plot?

Let’s add an image… In this example, I’ll show how to draw a ggplot2 plot with an image on top. We first need to load an image to R using the png package. For this, we have to install and load the png package first: Now, we can use the readPNG function to load an image that we have stored on our computer:

How to visualize transparent background in a plot in R?

To actually visualize a transparent background the image has to be stored as a PNG image, this can be done ggsave () function. In this approach, after the plot has been created normally, theme () function is added to it with rect parameter. To rect, element_rect () function is passed with parameter fill set to transparent.

How to put a background image in R?

It is done thanks to the jpeg R package. First of all, find an image. If the image is on the web, you can download it with download.file (). Then, load the image on R with the png library nd the readPNG () function. Last, it is the rasterImage () function that allows to put it on the background. Note: if you have a jpeg file.

How to export a ggplot2 plot with transparent rectangle elements?

Example 1 shows how to export a ggplot2 plot with transparent rectangle elements. Next, we can use the ggsave function to write our transparent plot to a PNG file: Have a look at your current working directory after running the previous R code. You should find a ggplot2 plot with transparent background.


2 Answers

You can set width and height arguments in rasterGrob both equal to 1 "npc", which will force the image to fill the plot area. Then specify the height and width of the image when you save it to get the aspect ratio you desire. Theme and scale_y_reverse options can be used to control the appearance of the axes as demonstrated also below. note that we can also use the expand parameter to ensure that the axes do not extend further than the image or data.

g <- rasterGrob(img, width=unit(1,"npc"), height=unit(1,"npc"), interpolate = FALSE)

g_ct <- ggplot(data=df_ct) +
  annotation_custom(g, -Inf, Inf, -Inf, Inf) +
  geom_path(aes_string(x=df_ct$X1, y=df_ct$X0), color='red', size=1) +
  scale_y_reverse("", 
                  labels = c(min(df_ct$X0), rep("", length(seq(min(df_ct$X0), max(df_ct$X0), 5))-2),max(df_ct$X0)), 
                  breaks = seq(min(df_ct$X0), max(df_ct$X0), 5),
                  expand = c(0,0)) +
  theme(plot.margin = unit(c(5,5,5,5), "mm"),
        axis.line.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.line.y = element_blank(),
        axis.ticks.y = element_line(size = 1),
        axis.ticks.length = unit(5,'mm')) +
  scale_x_continuous("")
g_ct

ggsave('test.png', height=5, width = 2, units = 'in')

Some data:

df_ct <- data.frame(X0 = 0:100)
df_ct$X1 = sin(df_ct$X0) +rnorm(101)

and a background image:

https://i.stack.imgur.com/aEG7I.jpg
like image 151
dww Avatar answered Sep 18 '22 12:09

dww


Addition to the answer by @dww.
If you need to overlay (or rather 'under'lay) the image exactly over data points then instead of annotation_custom(g, -Inf, Inf, -Inf, Inf)
use
annotation_custom(g, minX, maxX, minY, maxY)
with minX..maxY being the bottom left to top right corner of where the image corners lay on your data.

You can then also freely zoom your data with xlim(), ylim() without loosing the image-data position relationship.

example (download of image per this SO question)

download.file("https://upload.wikimedia.org/wikipedia/commons/b/b9/Sinusoidal_projection_SW.jpg",
              destfile="tmp.jpg", mode="wb")
world_image <- jpeg::readJPEG("tmp.jpg")
ggplot(FileWellsUsed, aes(y=Decimal.Latitude))   +
     annotation_custom(rasterGrob(world_image, 
                                  width = unit(1,"npc"), 
                                  height = unit(1,"npc")), 
                       -180, 180, -90, 90)+
       ggtitle("Sinusoidal projection") + 
       xlim(c(-180,180)) +ylim(c(-60,75))+
       coord_fixed() +
       ...yourdata.....

Also see this question, which uses rasterGrid instead of rasterGrob.

like image 32
Robbes Avatar answered Sep 22 '22 12:09

Robbes