Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brainstorming for isolines_grob() API #3

Closed
clauswilke opened this issue Feb 15, 2019 · 8 comments
Closed

Brainstorming for isolines_grob() API #3

clauswilke opened this issue Feb 15, 2019 · 8 comments

Comments

@clauswilke
Copy link
Collaborator

clauswilke commented Feb 15, 2019

The isoband_grob() function generates a grob that can draw labeled isolines:

library(isoband)
library(grid)

x <- (1:ncol(volcano))/(ncol(volcano)+1)
y <- (nrow(volcano):1)/(nrow(volcano)+1)
lines <- isolines(x, y, volcano, 5*(20:38))

# draw labeled lines
grid.newpage()
grid.draw(isolines_grob(lines, breaks = 20*(5:10)))

The question is how to best provide graphical parameters. Currently, there is one gpar() argument, which applies to lines and text equally, and it recycles settings both among and within isolines:

grid.newpage()
grid.draw(
  isolines_grob(
    lines, breaks = 20*(5:10),
    gp = gpar(
      col = c("red", "green", "blue"),
      fontfamily = "Times", lty = 2, fontsize = 12
    )
  )
)

Clearly we need to do better. There are a couple of different options:

  1. Provide a list of gpar() arguments, one for each isoline, and one separately for text.
  2. Use only one gpar() argument but recycle graphical parameters only among isolines, never within.
  3. Provide graphical parameters in a large table, with one row per isoline and one column per parameter (color, line width, etc)

There's also the separate question of whether it is reasonable to assume that the font settings (font face, family, size) are constant throughout the entire grob or whether we should be able to change those settings for different labels. The current code allows these things to vary among individual labels, mostly by accident.

grid.newpage()
grid.draw(
  isolines_grob(
    lines, breaks = 20*(5:10),
    gp = gpar(
      fontfamily = c("Times", "Helvetica"),
      fontface = 1:4, 
      fontsize = c(12, 14, 20)
    )
  )
)

@yutannihilation
Copy link

Is it possible to make the lines and texts seperate grobs? Then, we can specify gpar() for them seperately. (Disclaimer: I'm not familiar with designing a function for grid...)

@clauswilke
Copy link
Collaborator Author

No. I need to clip the lines to the extent of the text labels, and I need to do so dynamically if the window is resized etc. As a consequence, lines and text labels need to be drawn together.

As I'm thinking more about it, I'm thinking that maybe option 2 is best. It would be most similar to how other grobs work. I would need one extra argument that allows users to override the text color in case they want it to be different from the line colors. Other than that, none of the line parameters overlap with the text parameters, as far as I can see.

@yutannihilation
Copy link

I need to do so dynamically

Ah, I got it! Thanks. Then I agree option 2 + the text colour is enough.

@clauswilke
Copy link
Collaborator Author

Apart from some grid awkwardnesses, coding this wasn't too bad, and I think the behavior is reasonably logical.

library(isoband)
library(grid)

x <- (1:ncol(volcano))/(ncol(volcano)+1)
y <- (nrow(volcano):1)/(nrow(volcano)+1)
lines <- isolines(x, y, volcano, 5*(20:38))

# draw labeled lines
g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    lwd = c(2, 1, 1, 1),
    col = c("red", "green", "blue"),
    fontfamily = "Times", fontsize = c(8, 10, 12, 14, 16)
  )
)

grid.newpage()
pushViewport(viewport(gp = gpar(fontface = "bold", lty = c(1, 2, 2, 2))))
grid.draw(g)

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    lwd = c(2, 1, 1, 1),
    col = c("grey20", "grey50", "grey50", "grey50")
  ),
  label_col = "blue"
)

grid.newpage()
grid.draw(g)

Created on 2019-02-17 by the reprex package (v0.2.1)

@clauswilke
Copy link
Collaborator Author

I'm quite satisfied with this. I think it'll work as needed.

library(isoband)
library(grid)

viridis_pal <- colorRampPalette(
  c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"),
  space = "Lab"
)

x <- (1:ncol(volcano))/(ncol(volcano)+1)
y <- (nrow(volcano):1)/(nrow(volcano)+1)
lines <- isolines(x, y, volcano, 5*(19:38))
bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39))

b <- isobands_grob(
  bands,
  gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4)
)
l <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    lwd = c(.3, 1, .3, .3)
  )
)

grid.newpage()
grid.draw(b)
grid.draw(l)

Created on 2019-02-17 by the reprex package (v0.2.1)

@eliocamp
Copy link
Contributor

eliocamp commented Mar 6, 2019

This is stellar work! I should deprecate geom_contour_fill() and related functions from metR 😭️.

If I may chime in, is there a way of customising label placing and overall behaviour? For example, setting if text should be rotated to follow lines or not.

@clauswilke
Copy link
Collaborator Author

That doesn't currently exist but it's on my todo list. I want the code to be modular so it's easy to change the logic of the label placement.

@clauswilke
Copy link
Collaborator Author

I now have a basic framework that allows customization of label placement. More sophisticated or alternative label placement strategies can be implemented in the future. I'm closing this issue because I think the overall API is fine at this point.

library(isoband)
library(grid)

viridis_pal <- colorRampPalette(
  c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"),
  space = "Lab"
)

x <- (1:ncol(volcano))/(ncol(volcano)+1)
y <- (nrow(volcano):1)/(nrow(volcano)+1)
lines <- isolines(x, y, volcano, 5*(19:38))
bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39))

b <- isobands_grob(
  bands,
  gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4)
)
l <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    lwd = c(.3, 1, .3, .3)
  ),
  label_placer = label_placer_minmax(placement = "tl", rot_adjuster = angle_fixed())
)

grid.newpage()
grid.draw(b)
grid.draw(l)

Created on 2019-04-01 by the reprex package (v0.2.1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants