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

Save Locally Generated Plot Locally As PNG/JPG #311

Closed
robertleitner opened this issue Nov 25, 2015 · 16 comments
Closed

Save Locally Generated Plot Locally As PNG/JPG #311

robertleitner opened this issue Nov 25, 2015 · 16 comments

Comments

@robertleitner
Copy link

How can I save an interactive plot generated by plotly locally as png? The commands png()...dev.off() won't capture the plot command and the function call plotly_IMAGE throws an authorization error although the plot is generated locally and I'm logged in.

Thanks for your help.

@cpsievert
Copy link
Collaborator

Hmm, plotly_IMAGE() should work...does plotly_POST() work for you? If not, you probably haven't set your credentials properly. You can use plotly:::verify("username") and plotly:::verify("api_key") to double-check the R package can find them.

You can also open the interactive viz in a web browser and use plotly's "download as plot png" button (this isn't yet working in RStudio, but we're working on it)

screen shot 2015-11-25 at 9 32 06 pm

@rodrigomurta
Copy link

Here is a SO related post: exporting png files from plotly in r without internet connection
The idea is to create it without UI interaction and with no internet connection.
plotly_POST() works, but need internet. I believe that with the new Open JS Strategy that this functionality make sense.
tks

@cpsievert
Copy link
Collaborator

You could use something like RSelenium to automate this, but I won't be adding any native support to plotly's R package.

@xzmagic
Copy link

xzmagic commented Feb 26, 2016

does plotly_IMAGE only work with username and api_key?

@rodrigomurta
Copy link

@xzmagic unfortunately yes. The conversion is created in Plotly server, not locally.

@cpsievert cpsievert reopened this Apr 7, 2016
@cpsievert
Copy link
Collaborator

@timelyportfolio bootstrapping off of discusion in plotly/plotly.js#83, what do you think about using https://github.com/jeroenooms/rsvg to produce bitmaps locally?

@cpsievert
Copy link
Collaborator

(of course I'd prefer avoiding an extra R dependency if a pure JS solution seems plausible)

@timelyportfolio
Copy link
Collaborator

Funny, I had the same idea this morning :) I started yesterday on very preliminary work to attach the toImage method to a plot. I think we could tie this in. However, until the nested svgs are eliminated, RStudio will still render them incorrectly. Perhaps I could demonstrate a method in a Gist of how we could do what you are saying with rsvg. Would that work?

Remember though also the newest knitr + webshot will handle a lot of these cases automatically.

@cpsievert
Copy link
Collaborator

That'd be great!

@timelyportfolio
Copy link
Collaborator

Here are two ways of accomplishing a static image of a Plotly graph. If using in rmarkdown, I highly recommend using the newest knitr to automatically convert htmlwidgets (see knitr News).

# In issue 311
#   there was some discussion how we might
#   get our plotly graph as a static image.
#   Below are two ways to accomplish this:
#    1.  Use webshot or do automatically with knitr
#          If you want more info about the auto-knitr
#          method, then please see
#          https://github.com/yihui/knitr/blame/c3085229f5a02506e40f21c1cecf6d8448f5caaa/NEWS.md#L7-L10
#
#    2.  Use Shiny gadgets and rsvg
#

Method #2 has the advantage of multiple export formats.

Webshot

######## Webshot ##################################
library(plotly)
#  devtools::install_github("wch/webshot")
library(webshot)
library(htmltools)
library(magrittr)

#  make a simple plotly graph to illustrate
ggp <- ggplotly(
  ggplot(cars, aes(speed,dist)) + geom_point(),
  height=400,
  width=400
)

png_webshot <- tempfile(fileext = ".png")

tagList(
  htmlwidgets:::as.tags.htmlwidget(as.widget(
    ggp
  ))
) %>%
  html_print %>%
  # get forward slash on windows
  normalizePath(.,winslash="/") %>%
  # replace drive:/ with drive:// so C:/ becomes C://
  gsub(x=.,pattern = ":/",replacement="://") %>%
  # appends file:/// to make valid uri
  paste0("file:///",.) %>%
  # screenshot it for lots of good reasons
  webshot( file = png_webshot, delay = 1 )

#  see our new png in your image viewer of choice
system(sprintf("open %s", shQuote(png_webshot)))

file2e28719021f9

Shiny gadgets and rsvg

######## Shiny gadgets and rsvg ###################

library(plotly)
library(rsvg)
library(shiny)
library(miniUI)
library(htmlwidgets)
library(htmltools)

# make a little Shiny miniUI gadget
#  borrowed some code from chemdoodle
#  https://github.com/zachcp/chemdoodle/blob/master/R/chemdoodle_sketcher_gadgets.R
plotly_gadget <- function(plotly_widget, height=NULL, width=NULL){

  ui <- miniPage(
    miniContentPanel(
      htmlwidgets:::as.tags.htmlwidget(as.widget(plotly_widget))
    ),

    gadgetTitleBar("Get Static Image...", right = miniTitleBarButton("done", "Done", primary = TRUE)),

    tags$script('
      document.getElementById("done").onclick = function() {
        var plotly_svg = Plotly.Snapshot.toSVG(
          document.querySelectorAll(".plotly")[0]
        );
        Shiny.onInputChange("plotly_svg", plotly_svg);
      };
    ')
  )

  server <- function(input, output, session) {
    observeEvent(input$done, { stopApp(input$plotly_svg) })
  }

  runGadget(ui,
            server,
            viewer =  dialogViewer("PlotlyStaticImage",
                                   width = width,
                                   height = height))
}

#  make a simple plotly graph to illustrate
ggp <- ggplotly(
  ggplot(cars, aes(speed,dist)) + geom_point(),
  height=400,
  width=400
)

#  run our gadget to get the svg
svg <- plotly_gadget(ggp)

#  use rsvg to convert our svg to png
#    feel free to do something other than png
png_gadget <- tempfile(fileext=".png")
rsvg_png(charToRaw(svg), png_gadget)


#  see our new png in your image viewer of choice
system(sprintf("open %s", shQuote(png_gadget)))

file2e2813587d40

@cpsievert
Copy link
Collaborator

Thanks @timelyportfolio. I think for now we'll just leave this be and hope plotly.js provides a solution. For the record, I think I prefer the webshot approach, but I don't want to depend on it.

@jackparmer
Copy link
Contributor

Quick PSA 🎤
plotly.js now has an officially supported API for offline image export:
https://plot.ly/javascript/static-image-export/
🎉

@yankev is is adding programmatic offline export support to the Python client in this PR:
plotly/plotly.py#494

@spencerlyon2 has added incredible offline image export for the Julia client described here:
http://spencerlyon.com/PlotlyJS.jl/manipulating_plots/#saving-figures

@timelyportfolio
Copy link
Collaborator

I will try to add this in over next couple of days.

@cpsievert
Copy link
Collaborator

@timelyportfolio can we get away with export without a full-blown DOM renderer?

I've added this function to the major update I've been working on locally (I'll start the pull request tomorrow probably). What do you think?

#' Export a plotly graph to a static file
#' 
#' @param p a plotly or ggplot object.
#' @param file a filename. See the file argument of \code{webshot::webshot} 
#' for valid extensions.
#' @param ... arguments passed onto \code{webshot::webshot}
#' @export
#' @examples \dontrun{
#' export(plot_ly(economics, x = ~date, y = ~pce))
#' }
export <- function(p, file = "plotly.png", ...) {
  if (system.file(package = "webshot") == "") {
    stop(
      'Please install the webshot package ',
      '(if not on CRAN, try devtools::install_github("wch/webshot"))'
    )
  }
  f <- basename(tempfile('plotly', '.', '.html'))
  on.exit(unlink(f), add = TRUE)
  html <- htmlwidgets::saveWidget(plotly_build(p), f)
  webshot::webshot(f, file, ...)
}

@timelyportfolio
Copy link
Collaborator

I don't see any way possible to export without a full-blown DOM renderer.

Excited to see your changes.

@cpsievert
Copy link
Collaborator

Closed via d2bf2a1.

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

6 participants