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

ggplot2 imports when used in a package #21

Closed
rmendels opened this issue Feb 11, 2020 · 10 comments
Closed

ggplot2 imports when used in a package #21

rmendels opened this issue Feb 11, 2020 · 10 comments

Comments

@rmendels
Copy link

I am having an issue when using ggnewscale from within a package. It is pretty difficult to make a reproducible example because it depends on being in my package, which is a new version of the package 'plotdap'. 'plotdap' has its own object, but embedded in that object is a ggplot object, so let's call the 'plotdap' object 'plot' and the ggplot object is then plot$ggplot. I import ggnewscale in my Description, and the line I am trying is:

plot$ggplot <- plot$ggplot + ggnewscale::new_scale_colour()

okay so if do that on a plot where I need to set a new scale I get the following:

> p1 <-  add_tabledap(p,
+                     sardines, 
+                     ~subsample_count
+ )
 Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'guide_colourbar' of mode 'function' was not found 
10.
get(as.character(FUN), mode = "function", envir = envir) 
9.
match.fun(paste("guide_", scale$guide, sep = "")) 
8.
FUN(X[[i]], ...) 
7.
lapply(scales, bump_aes_scale, new_aes = new_aes) 
6.
bump_aes_scales(plot$scales$scales, new_aes = object) 
5.
ggplot_add.new_aes(object, p, objectname) 
4.
ggplot_add(object, p, objectname) 
3.
add_ggplot(e1, e2, e2name) 
2.
`+.gg`(plot$ggplot, ggnewscale::new_scale("colour")) at add_tabledap.R#112
1.
add_tabledap(p, sardines, ~subsample_count) 

Now this is what leaves me mystified. If i now do

library(ggplot2)

and try the exact same command (the command is a 'plotdap' command more complicated than the one line, but that is where it fails) it works. So somehow a reference to something in ggplot2 is not being found. I am stumped on this one. I looked at the bump_aes_scale() function and don't see why that should make a difference. I haven't posted this new version of 'plotdap' yet because I have been experimenting with using 'ggnewscale', but I could post it. I could then give a complete reproducible example. Basically I am trying to overlay a table like example (points) on a grid.

I should add that the first plot 'p' because it uses 'sf' has both a scale_colour_gradientn and a scale_fill_gradientn but with ggplot2::guides(colour = "none").

I mention the late because I was wondering because bump_aes_scale() tests for this if for some reason it is not catching it.

If you have any suggestions would appreciate it.

-Roy

@rmendels
Copy link
Author

okay a reproducible example. To run this you will need to have installed the packages 'rerddap' and 'plotdap' and the packages they depend on.

library(rerddap)
library(plotdap)

murSST_west <- griddap(
  'jplMURSST41', 
  latitude = c(22, 51), 
  longitude = c(-140, -105),
  time = c('last', 'last'), 
  fields = 'analysed_sst'
)
p <-  add_griddap(plotdap(),
                  murSST_west, 
                  ~analysed_sst,
                  fill = 'thermal'
)

library(ggnewscale)
p$ggplot <- p$ggplot + new_scale_colour()
Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'guide_colourbar' of mode 'function' was not found

library(ggplot2)
p$ggplot <- p$ggplot + new_scale_colour()

This suggests that somewhere guide_colourbar is not properly imported, though I could be wrong.

@eliocamp
Copy link
Owner

It's a small wrinkle when geting functions. Thanks for bringing into attention, I'll see what I can do!

@rmendels
Copy link
Author

Thanks. I was able to isolate the line in your code where it was occurring, but it was beyond what I know. Let me know if you think you have a fix, I will test it. Otherwise in the examples in my package that use this, i will just note that ggplot2 must be loaded.

Thanks for your work on this. A lot of what we do uses overlays, and these have been difficult because of the restrictions of ggplot2

@eliocamp
Copy link
Owner

I think it should be fixed in the dev version. Could you install and test it with

devtools::install_github("eliocamp/ggnewscale@dev")

?

@rmendels
Copy link
Author

Just did two quick test, including the overlay which I have as an example which was causing this, and they worked fine. Perfect thank you. When you submit this to CRAN, what will be the version number? I will make certain my imports require that version or higher. If you have the time and feel like it, could you explain to me the issue. As I said, I looked at your code, found the line (it was in match.fun) but couldn't see what was doing it and was hoping to learn both the problem and the fix.

@eliocamp
Copy link
Owner

Yes, no problem.

match.fun()/get(mode = "function") searches for functions in the environment they are called. So match.fun("mean") searches the environment for a function called "mean" and returns it, but only if mean is defined in the environment in which match.fun() is being called.

The guide argument in ggplot2 scales can be a function or a character with the name of the function. By default it's "colourbar" or "legend" depending on the type of scale. The relevant functions are guide_colourbar() and guide_legend(), which are defined in the ggplot2 package.
Now, if ggplot2 is not loaded, then neither function is present in the environment, so match.fun("guide_colourbar") returns an error.

match.fun("guide_colourbar")
#> Error in get(as.character(FUN), mode = "function", envir = envir): object 'guide_colourbar' of mode 'function' was not found

library(ggplot2)
match.fun("guide_colourbar")
#> function (title = waiver(), title.position = NULL, title.theme = NULL, 
#>     title.hjust = NULL, title.vjust = NULL, label = TRUE, label.position = NULL, 
#>     label.theme = NULL, label.hjust = NULL, label.vjust = NULL, 
#>     barwidth = NULL, barheight = NULL, nbin = 20, raster = TRUE, 
#>     frame.colour = NULL, frame.linewidth = 0.5, frame.linetype = 1, 
#>     ticks = TRUE, ticks.colour = "white", ticks.linewidth = 0.5, 
#>     draw.ulim = TRUE, draw.llim = TRUE, direction = NULL, default.unit = "line", 
#>     reverse = FALSE, order = 0, available_aes = c("colour", "color", 
#>         "fill"), ...) 
#> {
#>     if (!is.null(barwidth) && !is.unit(barwidth)) 
#>         barwidth <- unit(barwidth, default.unit)
#>     if (!is.null(barheight) && !is.unit(barheight)) 
#>         barheight <- unit(barheight, default.unit)
#>     structure(list(title = title, title.position = title.position, 
#>         title.theme = title.theme, title.hjust = title.hjust, 
#>         title.vjust = title.vjust, label = label, label.position = label.position, 
#>         label.theme = label.theme, label.hjust = label.hjust, 
#>         label.vjust = label.vjust, barwidth = barwidth, barheight = barheight, 
#>         nbin = nbin, raster = raster, frame.colour = frame.colour, 
#>         frame.linewidth = frame.linewidth, frame.linetype = frame.linetype, 
#>         ticks = ticks, ticks.colour = ticks.colour, ticks.linewidth = ticks.linewidth, 
#>         draw.ulim = draw.ulim, draw.llim = draw.llim, direction = direction, 
#>         default.unit = default.unit, reverse = reverse, order = order, 
#>         available_aes = available_aes, ..., name = "colorbar"), 
#>         class = c("guide", "colorbar"))
#> }
#> <bytecode: 0x55f2a3c19680>
#> <environment: namespace:ggplot2>

Created on 2020-02-13 by the reprex package (v0.3.0)

In your example, you call new_scale(), which eventually calls match.fun(). match.fun() then looks for a function called "guide_colourbar". In which environment? Well, first it looks it up in the ggnewscale environment, and when it doesn't find it, goes up a level, and so forth until it reaches the global environment. There it finally finds it, if and only if ggplot2 was loaded (or if you had manually defined a function with that name).

The solution was for me to import both functions into the ggnewscale package here. This makes it so that they are available in the ggnewscale environment, so that match.fun() finds them there. (I also had to change it to get() because for some reason match.fun() worked interactively but failed in CMD R Check 🤷‍♀️️).

I hope this answer is clear enough. The whole environments stuff is rather convoluted and I'm not at all an expert in it.

@eliocamp
Copy link
Owner

Ah, sorry. I'll send a patch to CRAN today with the version 0.4.1.

@rmendels
Copy link
Author

Thanks for that and the explanation.

@eliocamp
Copy link
Owner

No problem :)
Version 0.4.1 is now on CRAN and in the master branch.

@rmendels
Copy link
Author

Thanks again.

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

2 participants