Skip to content

Instantly share code, notes, and snippets.

@natemiller
Last active January 3, 2021 22:25
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 natemiller/fe8fc166e715231a139c0f02a1fa15c5 to your computer and use it in GitHub Desktop.
Save natemiller/fe8fc166e715231a139c0f02a1fa15c5 to your computer and use it in GitHub Desktop.

Challenge

I long struggled with a convenient means of adding images, primarily logos, to maps and charts made in ggplot. All the approaches just seemed clunky, it was hard to size the and place the logo, or clipping had to be turned off, or the plot had to be saved and then reloaded to add the logo.

Then I came across the ggpmisc package by Pedro Aphalo (https://docs.r4photobiology.info/ggpmisc/) which has a couple really nice functions geom_grob_npc and geom_plot_npc that make adding images/logos/ggplot objects to ggplot maps and charts so simple. Below I illustrate a simple wrapper for easy logo placement.

add_image function

Quick function that serves as a wrapper around ggpmisc::geom_grob_npc for placing images or logos on ggplot objects. I have used this for adding objects (such a context mini-globes) to maps, and logos to both maps and charts The npc functionality is really nice for relative placement and sizing is easy. Note: The real breakthrough here is nothing I have written, but the really useful ggpmisc package.

Packages

library(png)
library(ggplot2)
library(dplyr)
library(palmerpenguins)
library(tibble)
library(ggpmisc)

Function to add image to ggplot object

Lots of extraneous fluff in here for positioning and differential positioning for maps and charts. I have found this useful, but all of this is really just a wrapper around geom_grob_npc

add_image <- function(plot_object,
                      image_object,
                      img_rel_size = 0.3,
                      xloc = NA,
                      yloc = NA,
                      img_position = "lowerright",
                      img_just = "inside") {

  # is it a map or a chart?
  is_map <- "crs" %in% names(plot_object$coordinates)

  image_ <- image_object

  # ------------------------------------
  # CHARTS:
  # specify logo location (which corner)
  # ------------------------------------
  if (is_map == FALSE) {
    if (is.na(xloc) & is.na(yloc)) {
      if (img_position == "upperleft") {
        x_ <- 0
        y_ <- 1
      } else if (img_position == "upperright") {
        x_ <- 1
        y_ <- 1
      } else if (img_position == "lowerright") {
        x_ <- 1
        y_ <- 0
      } else if (img_position == "lowerleft") {
        x_ <- 0
        y_ <- 0
      } else if (img_position == 'center') {
        x_ <- 0.5
        y_ <- 0.5
      } else {
        break
      }
    } else if (!is.na(xloc) & !is.na(yloc)) {
      x_ <- xloc
      y_ <- yloc
    } else {
      break
    }
    # ------------------------------------
    # specify logo justification
    # ------------------------------------
    if (img_just == "center") {
      vjust_ <- "center"
      hjust_ <- "center"
    }

    if (img_just == "outside") {
      if (img_position == "upperleft") {
        vjust_ <- "bottom"
        hjust_ <- "left"
      } else if (img_position == "upperright") {
        vjust_ <- "bottom"
        hjust_ <- "right"
      } else if (img_position == "lowerright") {
        vjust_ <- "top"
        hjust_ <- "right"
      } else if (img_position == "lowerleft") {
        vjust_ <- "top"
        hjust_ <- "left"
      }
    }

    if (img_just == "inside") {
      if (img_position == "upperleft") {
        vjust_ <- "top"
        hjust_ <- "left"
      } else if (img_position == "upperright") {
        vjust_ <- "top"
        hjust_ <- "right"
      } else if (img_position == "lowerright") {
        vjust_ <- "bottom"
        hjust_ <- "right"
      } else if (img_position == "lowerleft") {
        vjust_ <- "bottom"
        hjust_ <- "left"
      }
    }
  }
  # ------------------------------------
  # MAPS:
  # specify logo location (which corner)
  # ------------------------------------

  else {
    if (img_position == "upperleft") {
      vjust_ <- "bottom"
      hjust_ <- "left"
    } else if (img_position == "upperright") {
      vjust_ <- "bottom"
      hjust_ <- "right"
    } else if (img_position == "lowerright") {
      vjust_ <- "top"
      hjust_ <- "right"
    } else if (img_position == "lowerleft") {
      vjust_ <- "top"
      hjust_ <- "left"
    } else {
      break
    }
    # ------------------------------------
    # specify logo justification and placement
    # ------------------------------------

    if (is.na(xloc) & is.na(yloc)) {
      if (img_just == "inside") {
        if (img_position == "upperleft") {
          x_ <- 0.02
          y_ <- 0.8
        } else if (img_position == "upperright") {
          x_ <- 0.98
          y_ <- 0.8
        } else if (img_position == "lowerright") {
          x_ <- 0.98
          y_ <- 0.2
        } else if (img_position == "lowerleft") {
          x_ <- 0.02
          y_ <- 0.2
        } else if (img_position == 'center') {
        x_ <- 0.5
        y_ <- 0.5
        }
      } else if (img_just == "outside") {
        if (img_position == "upperleft") {
          x_ <- 0
          y_ <- 0.9
        } else if (img_position == "upperright") {
          x_ <- 1
          y_ <- 0.9
        } else if (img_position == "lowerright") {
          x_ <- 1
          y_ <- 0.08
        } else if (img_position == "lowerleft") {
          x_ <- 0
          y_ <- 0.08
        } else if (img_position == 'center') {
        x_ <- 0.5
        y_ <- 0.5
        }
      } else if (img_just == "center") {
        stop("Center justification on maps not supported")
      }
    }
    else if (!is.na(xloc) & !is.na(yloc)) {
      x_ <- xloc
      y_ <- yloc
    } else {
      break
    }
  }


  location_df <- tibble::tibble(
    x = x_,
    y = y_,
    width = img_rel_size,
    height = img_rel_size,
    grob = list(ggplot2::ggplotGrob(ggplot() +
                  annotation_custom(image_) +
                  theme_void()))
  )
  plot_object +
    ggpmisc::geom_grob_npc(
      data = location_df,
      aes(
        label = grob,
        npcx = x,
        npcy = y,
        vp.width = width,
        vp.height = height
      ),
      vjust = vjust_,
      hjust = hjust_
    )
}

Examples

Load an image

myurl <- "https://www.r-project.org/logo/Rlogo.png"
z <- tempfile()
download.file(myurl,z,mode="wb")
pic <- readPNG(z)
file.remove(z) # cleanup
FALSE [1] TRUE
# convert to a rasterGrob
rlogo <- grid::rasterGrob(pic, interpolate=TRUE)

Plot Examples

plot <- ggplot() +
  geom_point(
    data = palmerpenguins::penguins,
    aes(flipper_length_mm, bill_length_mm, color = species)
  )

# place image in lower left, inside the plot panel
add_image(plot_object = plot, 
          image_object = rlogo,
          img_rel_size = 0.3,
          img_position = "lowerleft",
          img_just = "inside")

# upper right, inside plot panel, and bigger
add_image(plot_object = plot, 
          image_object = rlogo,
          img_rel_size = 0.5,
          img_position = "upperright",
          img_just = "inside")

# place logo in the center of the plot
add_image(plot_object = plot, 
          image_object = rlogo, 
          img_rel_size = 0.3, 
          img_position = 'center',
          img_just = "center")

# place logo off-center
add_image(plot_object = plot, 
          image_object = rlogo, 
          img_rel_size = 0.3, 
          xloc = 0.8, 
          yloc = 0.5, 
          img_just = "center")

# place logo under the plot on right side
add_image(plot_object = plot + 
            theme(plot.margin = margin(1, 1, 20, 1, unit = "mm")), 
          image_object = rlogo, 
          img_rel_size = 0.3, 
          img_position = "lowerright", 
          img_just = "outside")

# combining img_position and xloc/yloc for alternate placement
# pipe the plot into the add_image function
(plot +
  theme(plot.margin = margin(1, 20, 1, 1, unit = "mm"))) %>%
  add_image(
    plot_object = .,
    image_object = rlogo,
    img_rel_size = 0.3,
    yloc = 0,
    xloc = 1,
    img_position = "lowerleft"
  ) 

Add to map

land_sf <- rnaturalearth::ne_countries(scale = 110, returnclass = 'sf')
(ggplot() +
  geom_sf(data = land_sf) +
  theme_minimal()) %>%
  add_image(plot_object = ., 
            img_rel_size = 0.2,
            image_object = rlogo,
            img_position = 'upperright',
            img_just = 'outside')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment