rshinyr-leafletr-mapviewwebshot

How to tell mapshot not to crop leaflet or mapview maps


I recently noticed that every time I saved a leaflet map to file as PNG or PDF, my saved map boundaries are narrower than what my shiny app mainPanel is showing. I looked into the mapshot() arguments but did not see anything related to that. The capture() function saves the maps exactly as the mainPanel but mapshot() doesn't. It seems that the zoom is not being captured from the mainPanel. Can someone point out what am I missing?

Below is a small reproducible shiny and the screenshot comparison:

library(shiny)
library(mapview)
library(leaflet)

ui <- fluidPage(
  sidebarLayout(
  sidebarPanel(
  actionButton("print_map", "Print Map")),
  mainPanel(
  leafletOutput("mymap"))
)
)

server <- function(input, output, session) {
  output$mymap <- renderLeaflet({
    mapview(breweries)@map
  })
  
  user.created.map <- reactive({
    
    mapview(breweries)@map %>%
      
      setView( lng = input$mymap_center$lng
               ,  lat = input$mymap_center$lat
               , zoom = input$mymap_zoom
      )
    
  }) 
  
  observeEvent(input$print_map, {
    mapshot2(user.created.map(), 
             file = "map_zoom.png", remove_controls = c("zoomControl", "layersControl")
    )
  })
}

shinyApp(ui, server)

Screenshot on top is my mainPanel showing up to Weaverville and the bottom screenshot cuts about 2 inches of the mainPanel showing half of Lewiston.

enter image description here

enter image description here


Solution

  • mapshot captures the default viewport which does not necessarily match your app's actual container dimensions.

    Here, I am capturing dimensions of the leaflet container (both initially and on resize). I then use those dimensions in mapshot2().

    library(shiny)
    library(mapview)
    library(leaflet)
    library(rlang)
    
    ui <- fluidPage(
      tags$head(
        tags$script(HTML("
          $(document).on('shiny:connected', function() {
            function captureMapDimensions() {
              var mapContainer = $('#mymap');
              if (mapContainer.length > 0) {
                var width = Math.round(mapContainer.width());
                var height = Math.round(mapContainer.height());
                console.log('Capturing dimensions:', width, 'x', height);
                
                Shiny.setInputValue('map_width', width);
                Shiny.setInputValue('map_height', height);
              }
            }
            
            // capture on initial map render
            $(document).on('shiny:value', function(event) {
              if (event.name === 'mymap') {
                setTimeout(captureMapDimensions, 500);
              }
            });
            
            // capture on window resize
            $(window).on('resize', function() {
              setTimeout(captureMapDimensions, 100);
            });
          });
        "))
      ),
      sidebarLayout(
        sidebarPanel(
          actionButton("print_map", "Print Map"),
          h4("Current Dimensions:"),
          textOutput("cur_dimensions")
        ),
        mainPanel(
          leafletOutput("mymap", height = "500px")
        )
      )
    )
    
    server <- function(input, output, session) {
      output$mymap <- renderLeaflet({
        mapview(breweries)@map
      })
      
      output$cur_dimensions <- renderText({
        paste("Width:", input$map_width %||% "Not captured",
              "Height:", input$map_height %||% "Not captured")
      })
      
      user.created.map <- reactive({
        mapview(breweries)@map %>%
          setView(lng = input$mymap_center$lng,
                  lat = input$mymap_center$lat,
                  zoom = input$mymap_zoom)
      }) 
      
      observeEvent(input$print_map, {
        width <- as.integer(input$map_width %||% 1245)
        height <- as.integer(input$map_height %||% 500)
        
        cat("Mapshot dimensions:", width, "x", height, "\n")
        
        mapshot2(user.created.map(), 
                 file = "map_zoom.png", 
                 remove_controls = c("zoomControl", "layersControl"),
                 vwidth = width,
                 vheight = height
        )
      })
    }