I'm trying to save an image of my Leaflet map in my Shiny app. I'm using the mapshot function from the mapview package The map is constantly updated with markers etc via leafletProxy("map") %>% <add markers etc>
The issue appears to be with the fact the "map" is not actually a leaflet object, but rather a shiny output.
If you create the leaflet object in its own reactive expression and then pass that into the renderLeaflet and first mapshotobserver then that first save button works.
For the proxy option, I am not able to get it to work as I am getting the following error:
Warning: Error in system.file: 'package' must be of length 1
This seems to be an issue with saveWidget which is what mapshot calls. I am also not entirely sure what you are accomplishing by saving it with the proxy option that is not already being captured by the first save option. But I may be missing something.
Anyway, Here is the app with a working save button for the first option:
As a final note, please indicate when you have cross-posted your question (here is your question also on SO). This will prevent people from spending time trying to answer your question if you have already gotten an answer. Please see the FAQ about cross-posting questions:
Thanks for the reply, apologies for the cross posting!
I've given your way a go but I still don't get the markers that are added to the map on the exported map. That's the main issue, I'd like the user to be able to add their data as they go along and then export the map.
Marking this as solved as @tbradley helped me get the mapshot actually generating a file. I've since found https://github.com/rowanwins/leaflet-easyPrint which seems to do a better job (For my needs) at producing an export.
I was having the same issues as @ciaranevans. I used the leafletProxy function to give the user the flexibility to change the underlying data without losing all of the work they did navigating to a certain location, and you can't get that when the markers/shapefiles are added as part of the initial rendering as a reactive expression.
I was able to get the above example working by adding reactive values to store the points, and creating another reactive map that pulls from what's on the screen. It may be redundant, but it seems to work:
library(leaflet)
library(mapview)
library(shiny)
ui <- fluidPage(
titlePanel("Mapshot error example"),
sidebarLayout(
sidebarPanel(
actionButton("addMarkers", "Add markers"),
actionButton("saveMapUsingID", "Screenshot map with ID")
# actionButton("saveMapUsingProxy", "Screenshot map with Proxy")
),
mainPanel(
leafletOutput("map")
)
)
)
server <- function(input, output, session) {
# Create placeholder for reactive values that we'll update
v = reactiveValues()
v$point = NULL
# Create empty basemap of the world
map_reactive <- reactive({
leaflet() %>%
addProviderTiles(providers$OpenStreetMap)
})
# Call the reactive map of the world to render on the screen
output$map <- renderLeaflet({
map_reactive()
})
# Create long/lat points in London if the button is pushed
observeEvent(input$addMarkers, {
# Somewhere in London
p = data.frame(lng = runif(1, min=-0.34, max=0.16),
lat = runif(1, min=51.34, max=51.67))
v$point = rbind(v$point,p)
})
# Add lat/long created with button push to map and zoom in
observe({
if (!is.null(v$point)) {
leafletProxy("map") %>%
clearMarkers() %>%
addMarkers(lng=v$point[,1], lat=v$point[,2]) %>%
setView(lng=mean(v$point[,1]), lat=mean(v$point[,2]), zoom=10)
}
})
# Create a separate map object for printing - same properties as what's on the screen
user_created_map <- reactive({
m = map_reactive() %>%
setView(lng = input$map_center$lng, lat = input$map_center$lat,
zoom = input$map_zoom)
if (!is.null(v$point)) {
m = m %>%
addMarkers(lng=v$point[,1], lat=v$point[,2])
}
m
})
# Print the map to the working directory
observeEvent(input$saveMapUsingID, {
mapshot(user_created_map(), file=paste0(getwd(), '/exported_map.png'))
})
}
shinyApp(ui = ui, server = server)
Hope this helps others who are running into the same issue.