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

2019-04-08 21:21发布

问题:

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:

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:

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.

回答1:

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


回答2:

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.