1

I am trying to label a straight line segment using annotate. However, setting the angle of the annotation to the slope of the line does not align the annotation to the line segment.

library(ggplot2)

ggplot(data.frame(x = seq(0, 14, 0.1)), aes(x = x)) + 
  stat_function(fun = function(x) {
    14 - x
  }, geom = "line") + 
  theme_bw() + 
  annotate(
    geom = "text", 
    x = 7.5, y = 7.5,
    label = "x + y = 14",
    angle = -45)  # NISTunits::NISTradianTOdeg(atan(-1))

This gives:

enter image description here

Can someone help explain this phenomenon, and how to fix this, i.e., align the annotation to have the same angle as the line segment?

tchakravarty
  • 10,736
  • 12
  • 72
  • 116
  • 3
    Because the x Axis does not have the same length as the y Axis. Therefore, the angle is not 45 ! If both axis had the same scale, it would work 100% – Kabulan0lak Sep 07 '16 at 13:13
  • @Kabulan0lak Great, that makes sense -- so the suggestion is that I should multiply the angle by the aspect ratio of the viewport? How could I do that programmatically? – tchakravarty Sep 07 '16 at 13:16
  • 1
    Does the view have a fixed size ? If so, you can calculate the ratio between axis x and y to have the proper angle, yes. – Kabulan0lak Sep 07 '16 at 13:22
  • @Kabulan0lak No, the viewport does not have a fixed aspect ratio. – tchakravarty Sep 07 '16 at 13:24
  • Then I don't know how to do it. You should probably update the chart on size change but I am not sure how to do it. Good luck ! – Kabulan0lak Sep 07 '16 at 13:25
  • 4
    You could just add `coord_fixed()` so that the aspect ratio is fixed... – Martin Schmelzer Sep 07 '16 at 13:41

1 Answers1

3

I believe this should give you what you want. See this answer for reference Get width of plot area in ggplot2.

#Plot so we can reference the viewport
ggplot(data.frame(x = seq(0, 14, 0.1)), aes(x = x)) + 
  stat_function(fun = function(x) {
    14 - x
  }, geom = "line")

#Get the currentVPtree and then convert the VP's height/width to inches
current.vpTree()
a <- convertWidth(unit(1,'npc'), 'inch', TRUE)
b <- convertHeight(unit(1,'npc'), 'inch', TRUE)

#Replot using the ratio of a/b to get the appropriate angle
ggplot(data.frame(x = seq(0, 14, 0.1)), aes(x = x)) + 
  stat_function(fun = function(x) {
    14 - x
  }, geom = "line") + 
  theme_bw()+ 
  annotate(
    geom = "text", 
    x = 7.5, y = 7.5,
    label = "x + y = 14",
    angle = (atan(a/b) * 180/pi) + 270) 

We basically get the viewport width/height and then use simple geometry (inverse tangent since we have both sides of the triangle) to calculate what the actual angle of the line is.

Result:

enter image description here

Community
  • 1
  • 1
Mike H.
  • 13,960
  • 2
  • 29
  • 39
  • When trying to match text slope to other elements in the plot it's helpful to add `+coord_fixed(a/b)` to set an aspect ratio, otherwise the angle of your text can change, for example if export your plot as an image or add/change a legend. – Brian Fisher Aug 10 '20 at 21:05