9

The picture should give a good idea of what I would want to achieve. Ideally, the resulting colour bar would keep a continuous nature, i.e., should be based on a continuous rather than discrete aesthetic, or at least come very close to behave like it.

This also means, not just the simple approach of just bringing the legend keys of a discrete fill close enough together.

Yes, I could create a fake legend, but I'd like a solution that modifies at the legend drawing level.

I would be also very happy with interesting grid hacks!

library(ggplot2)

ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_colorbar(ticks.colour = "black"))

That's photoshopped, apologies!. I don't want to create the arrow, just the continuous scale with separators instead of ticks enter image description here

The ultimate goal is to reproduce a color bar as orignially proposed in this thread enter image description here

However, this is not about creating the discrete gradient bar, this is just about the separators!

The discrete gradient bar is not the problem, there are already solutions out there (e.g., https://stackoverflow.com/a/62544405/7941188, https://stackoverflow.com/a/62556763/7941188, https://stackoverflow.com/a/50540633/7941188)

tjebo
  • 21,977
  • 7
  • 58
  • 94

2 Answers2

6

There definitely is an option with the extendable guide system introduced in ggplot v3.3.0. See example below:

library(ggplot2)

guide_longticks <- function(...) {
  guide <- guide_colorbar(...)
  class(guide) <- c("guide", "guide_longticks", "colorbar")
  guide
}

guide_gengrob.guide_longticks <- function(guide, theme) {
  dir <- guide$direction
  guide <- NextMethod()
  is_ticks <- grep("^ticks$", guide$layout$name)
  ticks <- guide$grobs[is_ticks][[1]]
  if (dir == "vertical") {
    ticks$x1 <- rep(tail(ticks$x1, 1), length(ticks$x1))
  } else {
    ticks$y1 <- rep(tail(ticks$y1, 1), length(ticks$y1))
  }
  
  guide$grobs[[is_ticks]] <- ticks
  guide
}

ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_longticks(ticks = TRUE, ticks.colour = "black"))

Created on 2020-06-24 by the reprex package (v0.3.0)

EDIT:

Also try this alternative constructor if you want flat colours in between ticks:

guide_longticks <- function(...) {
  guide <- guide_colorsteps(...)
  class(guide) <- c("guide", "guide_longticks", "colorsteps", "colorbar")
  guide
}

EDIT2:

The following gengrob function would also delete the smaller ticks, if you want cleaner vector files. It kind of assumes that they are the last half of the ticks though:

guide_gengrob.guide_longticks <- function(guide, theme) {
  dir <- guide$direction
  guide <- NextMethod()
  is_ticks <- grep("^ticks$", guide$layout$name)
  ticks <- guide$grobs[is_ticks][[1]]
  n <- length(ticks$x0)
  if (dir == "vertical") {
    ticks$x0 <- head(ticks$x0, n/2)
    ticks$x1 <- rep(tail(ticks$x1, 1), n/2)
  } else {
    ticks$y0 <- head(ticks$y0, n/2)
    ticks$y1 <- rep(tail(ticks$y1, 1), n/2)
  }
  
  guide$grobs[[is_ticks]] <- ticks
  guide
}
teunbrand
  • 33,645
  • 4
  • 37
  • 63
  • 2
    Your questions are often more interesting to answer than 'how do I make this stacked barchart?' :) – teunbrand Jun 24 '20 at 15:35
  • 1
    As long as guides keep returning ticks in the current format, the edit above should remove the extra ticks. – teunbrand Jun 24 '20 at 18:29
  • Well at that point you already have a complete guide ready for drawing. It just needs to be positioned in the right place in the plot, but isn't modified any further. – teunbrand Jun 24 '20 at 20:13
5

There's certainly a quick grid hack Tjebo, but it probably needs a bit of work to make it robust (indexing by name rather than by number):

library(ggplot2)
library(grid)

p <- ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_colorbar(ticks.colour = "black"))

gt <- ggplot_gtable(ggplot_build(p))

x1 <- gt$grobs[[which(gt$layout$name == "guide-box")]]$grobs[[1]]$grobs[[5]]$x1
x1[1:7] <- x1[8:14]
gt$grobs[[which(gt$layout$name == "guide-box")]]$grobs[[1]]$grobs[[5]]$x1 <- x1
grid.newpage()
grid.draw(gt)

Created on 2020-06-24 by the reprex package (v0.3.0)

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87