use npc units in annotate()
Asked Answered
A

2

6

I have a ggplot object. I would like to add some text with annotate(), and I would like to specify the coordinates of the text in npc units. Is this possible?

This minimal example demonstrates how text is ordinarily positioned with annotate():

library(ggplot2)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p + annotate("text", x = 30, y = 4.5, label = "hello")

I would like to achieve the same effect, but instead of specifying x and y in native coordinates, I would like to specify them with npc coordinates. For the purpose of this example, I am not worried about exactly translating x = 30 and y = 4.5 into npc units. I just want to know whether npc units can be used in annotate() at all.

There are two related strategies, but they're not entirely satisfactory:

  1. One can use npc units by specifying them to, say, grid::textGrob(). And one can then place the grob with annotation_custom(), as in this answer by @baptiste. But this solution is a bit more cumbersome than I would like.

  2. The "ggpmisc" package includes geom_text_npc(). But it doesn't yet work with annotate(). That is, annotate("text_npc", ...) doesn't seem to work. [Edit: it now works. See Pedro Aphalo's message below.]

There are also many related posts. In particular, Greg Snow has suggested using grid to create a viewport with the dimensions of p and then annotating that viewport. And @teunbrand has suggested a method that entails converting p to a "gtable" object (with ggplotGrob()) and then drawing that "gtable" object. Either of these strategies can probably be adapted to my purposes. But is there a more straightforward way to use npc coordinates with annotate()?

Agonized answered 4/9, 2020 at 13:12 Comment(3)
Thanks for reporting this. I added some minutes ago function annotate() to package 'ggpmisc'. This new annotate() overrides the original definition adding support for the npcx and npcy position aesthetics. I will submit to CRAN in a few days. Meanwhile the code is available from Bitbucket (just be aware that for historical reasons the current main branch is called "no-debug"). This makes annotate("text_npc", ...) usable.Buncombe
That is excellent news. Thank you for the package, and for this update.Agonized
The updated 'ggpmisc' version 0.3.6 has been accepted by CRAN a few minutes ago. It will still take a couple of days for binaries to become available, while updating from sources should be possible sooner.Buncombe
E
12

Personally, I would use Baptiste's method but wrapped in a custom function to make it less clunky:

annotate_npc <- function(label, x, y, ...)
{
  ggplot2::annotation_custom(grid::textGrob(
    x = unit(x, "npc"), y = unit(y, "npc"), label = label, ...))
}

Which allows you to do:

p + annotate_npc("hello", 0.5, 0.5)

enter image description here

Note this will always draw your annotation in the npc space of the viewport of each panel in the plot, (i.e. relative to the gray shaded area rather than the whole plotting window) which makes it handy for facets. If you want to draw your annotation in absolute npc co-ordinates (so you have the option of plotting outside of the panel's viewport), your two options are:

  1. Turn clipping off with coord_cartesian(clip = "off") and reverse engineer the x, y co-ordinates from the given npc co-ordinates before using annotate. This is complicated but possible
  2. Draw it straight on using grid. This is far easier, but has the downside that the annotation has to be drawn over the plot rather than being part of the plot itself. You could do that like this:
annotate_npc_abs <- function(label, x, y, ...) 
{
  grid::grid.draw(grid::textGrob(
    label, x = unit(x, "npc"), y = unit(y, "npc"), ...))
}

And the syntax would be a little different:

p 
annotate_npc_abs("hello", 0.05, 0.75)

enter image description here

Easton answered 4/9, 2020 at 13:31 Comment(0)
B
3

As of 'ggpmisc' (>= 0.3.6) the following code works as expected (in CRAN as of 2020-09-10).

library(ggpmisc)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
# default justification is "inward"
p + annotate("text_npc", npcx = 0.8, npcy = 0.75, label = "hello")
# same justification as default for "geom_text()"
p + annotate("text_npc", npcx = 0.8, npcy = 0.75, label = "hello",
             hjust = "center", vjust = "middle")
Buncombe answered 10/9, 2020 at 17:20 Comment(1)
Good tip! This lead me to geom_text_npc of the ggpp package (sister package to ggpmisc.Hominy

© 2022 - 2024 — McMap. All rights reserved.