Following on from this question, I am looking to save and download a leaflet map as a png or jpeg image. I have the following code but I keep getting an error.
ui <- fluidPage(
leafletOutput("map"),
downloadButton("dl")
)
server <- function(input, output, session) {
output$map <- renderLeaflet({
leaflet() %>%
addTiles()
})
output$dl <- downloadHandler(
filename = "map.png",
content = function(file) {
mapshot(input[["map"]], file = file)
}
)
}
shinyApp(ui = ui, server = server)
The error I get when I try to download (by clicking the button) is
Warning: Error in system.file: 'package' must be of length 1
Stack trace (innermost first):
65: system.file
64: readLines
63: paste
62: yaml.load
61: yaml::yaml.load_file
60: getDependency
59: widget_dependencies
58: htmltools::attachDependencies
57: toHTML
56: <Anonymous>
55: do.call
54: mapshot
53: download$func [#11]
4: <Anonymous>
3: do.call
2: print.shiny.appobj
1: <Promise>
Error : 'package' must be of length 1
Bonus points if you can tell me how to get this working with leafletProxy
.
Since 'leaflet' maps are interactive, the leaflet object being used in mapview::mapshot()
function must also be interactive. Accounting for this allows for the user to save their version of a leaflet map within the Shiny app.
# install necessary packages
install.packages( c( "shiny", "leaflet", "mapview" ) )
# load necessary packages
library( shiny )
library( leaflet )
library( mapview )
ui <- fluidPage(
leafletOutput( outputId = "map"),
downloadButton( outputId = "dl")
)
server <- function(input, output, session) {
# Create foundational leaflet map
# and store it as a reactive expression
foundational.map <- reactive({
leaflet() %>% # create a leaflet map widget
addTiles( urlTemplate = "https://{s}.tile.openstreetmap.se/hydda/base/{z}/{x}/{y}.png" ) # specify provider tile and type
}) # end of foundational.map()
# render foundational leaflet map
output$map <- leaflet::renderLeaflet({
# call reactive map
foundational.map()
}) # end of render leaflet
# store the current user-created version
# of the Leaflet map for download in
# a reactive expression
user.created.map <- reactive({
# call the foundational Leaflet map
foundational.map() %>%
# store the view based on UI
setView( lng = input$map_center$lng
, lat = input$map_center$lat
, zoom = input$map_zoom
)
}) # end of creating user.created.map()
# create the output file name
# and specify how the download button will take
# a screenshot - using the mapview::mapshot() function
# and save as a PDF
output$dl <- downloadHandler(
filename = paste0( Sys.Date()
, "_customLeafletmap"
, ".pdf"
)
, content = function(file) {
mapshot( x = user.created.map()
, file = file
, cliprect = "viewport" # the clipping rectangle matches the height & width from the viewing port
, selfcontained = FALSE # when this was not specified, the function for produced a PDF of two pages: one of the leaflet map, the other a blank page.
)
} # end of content() function
) # end of downloadHandler() function
} # end of server
# run the Shiny app
shinyApp(ui = ui, server = server)
# end of script #
Once you run the Shiny app, open the app in a new window.
Once in the browser, go ahead and click Download
. It took about ~3 seconds.
Once Download
has been clicked, you'll promptly see a PDF file wherever your downloaded files are stored on your machine.
My ideas sprung from the following posts:
Save leaflet map in Shiny
How to save a leaflet map in Shiny
Input/Events - Leaflet for R
Thanks to @blondeclover, there is no need to store the bounds
of the leaflet map when using setView()
. Instead, simply use input$MAPID_center$lng
and input$MAPID_center$lat
when using setView()
.
May be this would help:
server <- function(input, output, session) {
map <- reactiveValues(dat = 0)
output$map <- renderLeaflet({
map$dat <- leaflet() %>%
addTiles()
})
output$dl <- downloadHandler(
filename = "map.png",
content = function(file) {
mapshot(map$dat, file = file)
}
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With