title: How to place grobs with annotation_custom() at precise areas of the plot region?
body: <p>I am trying to reproduce the following [base R] plot with <strong>ggplot2</strong></p>
  
  <p><img src="https://i.stack.imgur.com/B8HZA.png" alt="enter image description here"></p>
  
  <p>I have managed most of this, but what is currently baffling me is the placement of the line segments that join the marginal rug plot on the right of the plot with the respective label. The labels have been draw (in the second figure below) via <code>anotation_custom()</code> and I have used @baptiste's trick of turning off clipping to allow drawing in the plot margin.</p>
  
  <p>Despite many attempts I am unable to place <code>segmentGrobs()</code> at the desired locations in the device such that they join the correct rug tick and label.</p>
  
  <p>A reproducible example is</p>
  
  <pre><code>y &lt;- data.frame(matrix(runif(30*10), ncol = 10))
  names(y) &lt;- paste0("spp", 1:10)
  treat &lt;- gl(3, 10)
  time &lt;- factor(rep(1:10, 3))
  
  require(vegan); require(grid); require(reshape2); require(ggplot2)
  mod &lt;- prc(y, treat, time)
  </code></pre>
  
  <p>If you don't have <strong>vegan</strong> installed, I append a <code>dput</code> of the fortified object at the end of the Question and a <code>fortify()</code> method should you wish to run the example and <code>fortify()</code> for handy plotting with <code>ggplot()</code>. I also include a somewhat lengthy function, <code>myPlt()</code>, that illustrates what I have working so far, which can be used on the example data set if you have the packages loaded and can create <code>mod</code>.</p>
  
  <p>I have tried quite a few options but I seem to be flailing in the dark now on getting the line segments placed correctly.</p>
  
  <p>I am not looking for a solution to the specific issue of plotting the labels/segments for the example data set, but a generic solution that I can use to place segments and labels programatically as this will form the basis of an <code>autoplot()</code> method for objects of <code>class(mod)</code>. I have the labels worked out OK, just not the line segments. So to the questions:</p>
  
  <ol>
  <li>How are the <code>xmin</code>, <code>xmax</code>, <code>ymin</code>, <code>ymax</code> arguments used when I want to place a segment grob containing a line running from data coord <code>x0</code>, <code>y0</code> to <code>x1</code>, <code>y1</code>?</li>
  <li>Perhaps asked a different way, how do you use <code>annotation_custom()</code> to draw segments <strong>outside</strong> the plot region between known data coords <code>x0</code>, <code>y0</code> to <code>x1</code>, <code>y1</code>?</li>
  </ol>
  
  <p>I would be happy to receive Answers that simply had any old plot in the plot region but showed how to add line segments between known coordinates <em>in the margin of the plot</em>.</p>
  
  <p>I'm not wedded to the use of <code>annotation_custom()</code> so if a better solution is available I'd consider that too. I do want to <em>avoid</em> having the labels in the plot region though; I think I can achieve that by using <code>annotate()</code> and extending the x-axis limits in the scale via the <code>expand</code> argument.</p>
  
  <h2>The <code>fortify()</code> method</h2>
  
  <pre><code>fortify.prc &lt;- function(model, data, scaling = 3, axis = 1,
                          ...) {
      s &lt;- summary(model, scaling = scaling, axis = axis)
      b &lt;- t(coef(s))
      rs &lt;- rownames(b)
      cs &lt;- colnames(b)
      res &lt;- melt(b)
      names(res) &lt;- c("Time", "Treatment", "Response")
      n &lt;- length(s$sp)
      sampLab &lt;- paste(res$Treatment, res$Time, sep = "-")
      res &lt;- rbind(res, cbind(Time = rep(NA, n),
                              Treatment = rep(NA, n),
                              Response = s$sp))
      res$Score &lt;- factor(c(rep("Sample", prod(dim(b))),
                            rep("Species", n)))
      res$Label &lt;- c(sampLab, names(s$sp))
      res
  }
  </code></pre>
  
  <h2>The <code>dput()</code></h2>
  
  <p>This is the output from <code>fortify.prc(mod)</code>:</p>
  
  <pre><code>structure(list(Time = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 
  3, 4, 5, 6, 7, 8, 9, 10, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
  NA), Treatment = c(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 
  3, 3, 3, 3, 3, 3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), Response = c(0.775222658013234, 
  -0.0374860102875694, 0.100620532505619, 0.17475403767196, -0.736181209242918, 
  1.18581913245908, -0.235457236665258, -0.494834646295896, -0.22096700738071, 
  -0.00852429328460645, 0.102286976108412, -0.116035743892094, 
  0.01054849999509, 0.429857364190398, -0.29619258318138, 0.394303081010858, 
  -0.456401545475929, 0.391960511587087, -0.218177702859661, -0.174814586471715, 
  0.424769871360028, -0.0771395073436865, 0.698662414019584, 0.695676522106077, 
  -0.31659375422071, -0.584947748238806, -0.523065304477453, -0.19259357510277, 
  -0.0786143714402391, -0.313283220381509), Score = structure(c(1L, 
  1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
  1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("Sample", 
  "Species"), class = "factor"), Label = c("2-1", "2-2", "2-3", 
  "2-4", "2-5", "2-6", "2-7", "2-8", "2-9", "2-10", "3-1", "3-2", 
  "3-3", "3-4", "3-5", "3-6", "3-7", "3-8", "3-9", "3-10", "spp1", 
  "spp2", "spp3", "spp4", "spp5", "spp6", "spp7", "spp8", "spp9", 
  "spp10")), .Names = c("Time", "Treatment", "Response", "Score", 
  "Label"), row.names = c("1", "2", "3", "4", "5", "6", "7", "8", 
  "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", 
  "20", "spp1", "spp2", "spp3", "spp4", "spp5", "spp6", "spp7", 
  "spp8", "spp9", "spp10"), class = "data.frame")
  </code></pre>
  
  <h2>What I've tried:</h2>
  
  <pre><code>myPlt &lt;- function(x, air = 1.1) {
      ## fortify PRC model
      fx &lt;- fortify(x)
      ## samples and species scores
      sampScr &lt;- fx[fx$Score == "Sample", ]
      sppScr &lt;- fx[fx$Score != "Sample", ]
      ord &lt;- order(sppScr$Response)
      sppScr &lt;- sppScr[ord, ]
      ## base plot
      plt &lt;- ggplot(data = sampScr,
                    aes(x = Time, y = Response,
                        colour = Treatment, group = Treatment),
                    subset = Score == "Sample")
      plt &lt;- plt + geom_line() + # add lines
              geom_rug(sides = "r", data = sppScr) ## add rug
      ## species labels
      sppLab &lt;- sppScr[, "Label"]
      ## label grobs
      tg &lt;- lapply(sppLab, textGrob, just = "left")
      ## label grob widths
      wd &lt;- sapply(tg, function(x) convertWidth(grobWidth(x), "cm",
                                                valueOnly = TRUE))
      mwd &lt;- max(wd) ## largest label
  
      ## add some space to the margin, move legend etc
      plt &lt;- plt +
          theme(plot.margin = unit(c(0, mwd + 1, 0, 0), "cm"),
                legend.position = "top",
                legend.direction = "horizontal",
                legend.key.width = unit(0.1, "npc"))
      ## annotate locations
      ##  - Xloc = new x coord for label
      ##  - Xloc2 = location at edge of plot region where rug ticks met plot box
      Xloc &lt;- max(fx$Time, na.rm = TRUE) +
          (2 * (0.04 * diff(range(fx$Time, na.rm = TRUE))))
      Xloc2 &lt;- max(fx$Time, na.rm = TRUE) +
          (0.04 * diff(range(fx$Time, na.rm = TRUE)))
      ## Yloc - where to position the labels in y coordinates
      yran &lt;- max(sampScr$Response, na.rm = TRUE) -
          min(sampScr$Response, na.rm = TRUE)
      ## This is taken from vegan:::linestack
      ## attempting to space the labels out in the y-axis direction
      ht &lt;- 2 * (air * (sapply(sppLab,
                          function(x) convertHeight(stringHeight(x),
                                                    "npc", valueOnly = TRUE)) *
                   yran))
      n &lt;- length(sppLab)
      pos &lt;- numeric(n)
      mid &lt;- (n + 1) %/% 2
      pos[mid] &lt;- sppScr$Response[mid]
      if (n &gt; 1) {
          for (i in (mid + 1):n) {
              pos[i] &lt;- max(sppScr$Response[i], pos[i - 1] + ht[i])
          }
      }
      if (n &gt; 2) {
          for (i in (mid - 1):1) {
              pos[i] &lt;- min(sppScr$Response[i], pos[i + 1] - ht[i])
          }
      }
      ## pos now contains the y-axis locations for the labels, spread out
  
      ## Loop over label and add textGrob and segmentsGrob for each one
      for (i in seq_along(wd)) {
          plt &lt;- plt + annotation_custom(tg[[i]],
                                         xmin = Xloc,
                                         xmax = Xloc,
                                         ymin = pos[i],
                                         ymax = pos[i])
          seg &lt;- segmentsGrob(Xloc2, pos[i], Xloc, pos[i])
  
          ## here is problem - what to use for ymin, ymax, xmin, xmax??
          plt &lt;- plt + annotation_custom(seg,
                                         ## xmin = Xloc2,
                                         ## xmax = Xloc,
                                         ## ymin = pos[i],
                                         ## ymax = pos[i])
                                         xmin = Xloc2,
                                         xmax = Xloc,
                                         ymin = min(pos[i], sppScr$Response[i]),
                                         ymax = max(pos[i], sppScr$Response[i]))
      }
      ## Build the plot
      p2 &lt;- ggplot_gtable(ggplot_build(plt))
      ## turn off clipping
      p2$layout$clip[p2$layout$name=="panel"] &lt;- "off"
      ## draw plot
      grid.draw(p2)
  }
  </code></pre>
  
  <h2>Figure based on what I have tried in <code>myPlt()</code></h2>
  
  <p>This is as far as I have made it with <code>myPlt()</code> from above. Note the small horizontal ticks drawn through the labels - these should be the angled line segments in the first figure above.</p>
  
  <p><img src="https://i.stack.imgur.com/Z7kwT.png" alt="enter image description here"></p>