Search code examples
rshinyr-leaflet

Is it possible to change leaflet map screenshot size in R Shiny?


I found two answers concerning saving Leaflet Maps in shiny:

  1. How to save a leaflet map in Shiny

  2. How to save Leaflet in R map as png or jpg file?

The core idea of them is to use mapshot() instead webshot(). In this case for setting screenshot size used cliprect parameter. It defines a clipping rectangle which matches the height & width from the viewing port. So, if cliprect = 'viewport', I thought, map on the screenshot will be looks like on my screen. But it isn't.

I tryed to use code from examples above and they have given the same result.

That's why my question is: Is it possible to change leaflet map screenshot size in R Shiny and if yes, how it can be done?

My screen in browser with map looks likes this

I hope to get screenshot like this

But I got this


Solution

  • Edit: The original answer downloaded the map as rendered initially without taking user's interaction with the map into account. I updated my answer using this answer so the download reflects the map as it looks to the user.

    1. Use js to get the current size of the map div (as based on this and this)
    2. Update the map based on current zoom and map center
    3. Change the size of the screenshot using vwidth and vheight

    Example based on this and this:

    library(leaflet)
    library(mapview)
    library(shiny)
    
    ui <- fluidPage(
      # 1. js to get width/height of map div
      tags$head(tags$script('
                            var dimension = [0, 0];
                            $(document).on("shiny:connected", function(e) {
                            dimension[0] = document.getElementById("map").clientWidth;
                            dimension[1] = document.getElementById("map").clientHeight;
                            Shiny.onInputChange("dimension", dimension);
                            });
                            $(window).resize(function(e) {
                            dimension[0] = document.getElementById("map").clientWidth;
                            dimension[1] = document.getElementById("map").clientHeight;
                            Shiny.onInputChange("dimension", dimension);
                            });
                            ')),
      leafletOutput("map"),
      downloadButton("dl", "Download Map")
      )
    
    server <- function(input, output, session) {
    
      # reactive values to store map
      vals <- reactiveValues()
    
      # create base map  
      output$map <- renderLeaflet({
        vals$base <- leaflet() %>%
          addProviderTiles(providers$OpenStreetMap) %>%
          addTiles()
      })
    
      # create map as viewed by user
      observeEvent({
        input$map_zoom
        input$map_center
        }, {
          vals$current <- vals$base %>% 
            setView(lng = input$map_center$lng,
                    lat = input$map_center$lat,
                    zoom = input$map_zoom)
        }
      )
    
      # create download
      output$dl <- downloadHandler(
        filename = "map.png",
    
        content = function(file) {
          mapshot(vals$current, file = file,
                  # 2. specify size of map based on div size
                  vwidth = input$dimension[1], vheight = input$dimension[2])
        }
      )
    
    }
    shinyApp(ui = ui, server = server)