3
votes

I have created an app where the user can modify a leaflet map and i would like to use this map in a pdf report. I have 1. installed packages leaflet, webshot and htmlwidget 2. installed PhantomJS

below is a simplified version of the code

server.R:

    library(shiny)
    library(leaflet)
    library(htmlwidgets)
    library(webshot)

    shinyServer(function(input, output, session) {

      output$amap <- renderLeaflet({
      leaflet() %>%
        addProviderTiles("Stamen.Toner",
                     options = providerTileOptions(noWrap = TRUE,      reuseTiles=TRUE))
  })

  observe({
    leafletProxy("amap") %>%
    clearShapes() %>%
    addCircles(lng = c(22,-2), lat = c(42,65))
  })



  observeEvent(input$saveButton,{
    themap<- leafletProxy("amap")
    saveWidget(themap, file="temp.html", selfcontained = F) 
    webshot("temp.html", file = "Rplot.png",
          cliprect = "viewport")

  })
})

ui.R:

fluidPage(
  leafletOutput("amap", height="600px", width="600px"),
  br(),
  actionButton("saveButton", "Save")
  )

I get this error message:

Warning: Error in system.file: 'package' must be of length 1 Stack trace (innermost first): 73: system.file 72: readLines 71: paste 70: yaml.load 69: yaml::yaml.load_file 68: getDependency 67: widget_dependencies 66: htmltools::attachDependencies 65: toHTML 64: saveWidget 63: observeEventHandler [C:\R files\test/server.R#24] 1: shiny::runApp

when the save button is activated. savewidget works fine when i define the save button like this:

  observeEvent(input$saveButton,{
    themap<-leaflet() %>%
      addProviderTiles("Stamen.Toner",
                       options = providerTileOptions(noWrap = TRUE, reuseTiles=TRUE))

    saveWidget(themap, file="temp.html", selfcontained = F) 
    webshot("temp.html", file = "Rplot.png",
          cliprect = "viewport")

  })

But i really want the changes that the user makes in the webshot. Can anyone help?

1
do you want the user to be able to save the file? I think this would be better handled on the JavaScript side. I'll try to work up an example for you.timelyportfolio
well, my brilliant solution gets tangled up in cross-origin problems. I'll try to think of another way to handle.timelyportfolio

1 Answers

5
votes

This is not perfect, but here a solution using the library html2canvas. Please be careful of attribution, license, and copyright. Also, this won't work in RStudio Viewer, but there are ways to get it to work.

library(leaflet)
library(htmltools)

lf <- leaflet() %>%
  addProviderTiles(
    "Stamen.Toner",
    options = providerTileOptions(
      noWrap = TRUE,
      reuseTiles=TRUE
    )
  )


#  add the mapbox leaflet-image library
#   https://github.com/mapbox/leaflet-image
#lf$dependencies[[length(lf$dependencies)+1]] <- htmlDependency(
#  name = "leaflet-image",
#  version = "0.0.4",
#  src = c(href = "http://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-image/v0.0.4/"),
#  script = "leaflet-image.js"
#)

lf$dependencies[[length(lf$dependencies)+1]] <- htmlDependency(
  name = "html2canvas",
  version = "0.5.0",
  src = c(href="https://cdn.rawgit.com/niklasvh/html2canvas/master/dist/"),
  script = "html2canvas.min.js"
)



browsable(
  tagList(
    tags$button("snapshot",id="snap"),
    lf,
    tags$script(
'
document.getElementById("snap").addEventListener("click", function() {
  var lf = document.querySelectorAll(".leaflet");
  html2canvas(lf, {
    useCORS: true,
    onrendered: function(canvas) {
      var url = canvas.toDataURL("image/png");
      var downloadLink = document.createElement("a");
      downloadLink.href = url;
      downloadLink.download = "map.png"

      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink); 
    }
  });
});
'      
    )
  )
)