rggplot2r-sfmagick

Plotting points on an imported image


I'm trying to plot points on top of an imported image, made with ggplot2 and geom_sf().

When I do a mock-up (first chunk of code) and import an image that's a simple ggplot(), everything works - the added points sit right where they should be. But once I switch to using geom_sf(), something is obviously shifted - the points are in the correct spot on the y-axis, but not the x-axis.

library(ggplot2)
library(magick)
library(sf)

df <- data.frame(x = 1:10, y = 1:10)
ggplot(df) +
    geom_point(aes(x = x, y = y)) +
    theme_void() +
    scale_x_continuous(breaks = 1:10, limits = c(0, 11), expand = c(0, 0)) +
    scale_y_continuous(breaks = 1:10, limits = c(0, 11), expand = c(0, 0))

ggsave("My image.png", width = 5, height = 5, units = "in")

img <- image_read("My image.png")
plot(NULL, xlim = c(0, 11), ylim = c(0, 11), axes=FALSE, xlab = "", ylab = "")
rasterImage(img, 0, 0, 11, 11)
points(df$x, df$y, col = "red")

The same approach, but with geom_sf() and the image fails - the points are plotted, but are offset from their true position (should be plotted on top of the 2 corners).

rect <- data.frame(lon = c(-94.9, -93), lat = c(32, 35))
rect_sf <- st_polygon(list(cbind(
             rect$lon[c(1,2,2,1,1)],
             rect$lat[c(1,1,2,2,1)]))) %>%
        st_sfc(crs = 4326)

ggplot() +
    geom_sf(data = rect_sf, fill = "grey85", colour = "black") +
    theme_void() +
    coord_sf(xlim = rect$lon, ylim = rect$lat, expand = FALSE)
ggsave("My image.png", width = 5, height = 5, units = "in")

img <- image_read("My image.png")
plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "")
rasterImage(img, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2])
points(rect$lon, rect$lat, col = "red")

I think the issue is the aspect ratio of the saved image. Below is the same code with 2 different saving options - one wide, one long. The resulting offset of the red points plotted post-image-read changes accordingly. I think that the "offset" is the white space added around the "offending" axis when the image is generated with the wrong aspect ratio. How do I specify the correct aspect ratio in these cases?

rect <- data.frame(lon = c(-94.9, -93), lat = c(32, 35))
rect_sf <- st_polygon(list(cbind(
             rect$lon[c(1,2,2,1,1)],
             rect$lat[c(1,1,2,2,1)]))) %>%
        st_sfc(crs = 4326)

ggplot() +
    geom_sf(data = rect_sf, fill = "grey85", colour = "black") +
    theme_void() +
    coord_sf(xlim = rect$lon, ylim = rect$lat, expand = FALSE)
ggsave("My image.png", width = 8, height = 2, units = "in")
ggsave("My image.png", width = 3, height = 9, units = "in")

img <- image_read("My image.png")
plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "")
rasterImage(img, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2])
points(rect$lon, rect$lat, col = "red")

Solution

  • you need to trim the whitespace around your plot, using something like magick's image_trim()

    img <- image_read("My image.png")
    img_trimmed <- magick::image_trim(img)
    plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "")
    rasterImage(img_trimmed, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2])
    points(rect$lon, rect$lat, col = "red")