Skip to content

Save Locally Generated Plot Locally As PNG/JPG #311

Closed

Description

@robertleitner

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.

Activity

  1. cpsievert commented on Nov 25, 2015

    @cpsievert
    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

  2. rodrigomurta commented on Nov 30, 2015

    @rodrigomurta

    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

  3. cpsievert commented on Nov 30, 2015

    @cpsievert
    Collaborator

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

  4. xzmagic commented on Feb 26, 2016

    @xzmagic

    does plotly_IMAGE only work with username and api_key?

  5. rodrigomurta commented on Mar 4, 2016

    @rodrigomurta

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

  6. cpsievert commented on Apr 7, 2016

    @cpsievert
    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?

  7. cpsievert commented on Apr 7, 2016

    @cpsievert
    Collaborator

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

  8. timelyportfolio commented on Apr 7, 2016

    @timelyportfolio
    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.

  9. cpsievert commented on Apr 7, 2016

    @cpsievert
    Collaborator

    That'd be great!

  10. timelyportfolio commented on Apr 7, 2016

    @timelyportfolio
    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

  11. cpsievert commented on Apr 7, 2016

    @cpsievert
    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.

  12. jackparmer commented on Jun 10, 2016

    @jackparmer
    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

  13. timelyportfolio commented on Jun 11, 2016

    @timelyportfolio
    Collaborator

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

  14. cpsievert commented on Jun 13, 2016

    @cpsievert
    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, ...)
    }
  15. timelyportfolio commented on Jun 13, 2016

    @timelyportfolio
    Collaborator

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

    Excited to see your changes.

  16. cpsievert commented on Jul 14, 2016

    @cpsievert
    Collaborator

    Closed via d2bf2a1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions