rleaflet

Is it possible to auto-adjust the zoom when rendering large leaflet maps into png?


With the following code one can generate a leaflet map:

library(leaflet)
library(mapview)
library(dplyr)
library(sf)

bbox <- st_bbox(my_polygon)
print(bbox)

      xmin       ymin       xmax       ymax 
-0.5509286 39.4419540 -0.4397764 39.4779036 

poly <- st_as_sfc(bbox, crs = 4326)

leaflet() %>%
  addTiles() %>%
  addPolygons(data = poly, opacity = 1, fillOpacity = 1, fillColor = 'red') %>%
  mapshot(file = 'example.png', vwidth = 9256, vheight = 6600)

However, when viewing the image, you can tell there's a lot of "blank space" (see current output)

No matter what I try (e.g: setMaxBounds, fitBounds, a bunch of onRender combinations I've seen over here, etc) the map's zoom won't be adjusted properly, and what do I mean by "properly adjusted"?, what the actual aforementioned functions (setMaxBounds and fitBounds) should do (see desired output). And yes, the image is required to be that large.


Solution

  • Leaflet has a parameter called zoomSnap = 1 = which is the amount by which you can zoom by one scroll operation. Typically, you can zoom from 0 (all zoomed out) to 20. In this case, your leaflet widget becomes very large though, because you basically force the browser into the dimension 9256px * 3966px. So a zoomSnap of 1 is just too much - the gap becomes to wide. To still keep the polygon in frame leaflet has to choose a smaller zoomLevel. So my thought was, to set zoomSnap down. This worked, but mapshot() was still capturing the wrong image. I changed to mapshot2 which fixed this issue. Simplified: mapshot uses webshot under the hood, it basically generates a leaflet widget, opens it in a browser, sets the window size and then takes an image of a specific DOM-element; in this case the map. You have to keep this in mind, when working with it. fitBounds does not quite work as expected. It flies you to the mean of lon/lat, and sets the zoom level in a range that keeps the given bounds visible.

    Note: The smaller you set zoomSnap, the narrower the edge will become.

    # R version 4.5.1 (2025-06-13 ucrt)
    library(leaflet) # leaflet_2.2.3      
    library(mapview) # mapview_2.11.4 
    library(sf)      # sf_1.0-21 
    
    bb <- sf::st_bbox(c(xmin = -0.5509286, xmax = -0.4397764, ymin = 39.4419540, ymax = 39.4779036))
    poly <- sf::st_as_sfc(bb)
    
    leaflet(options = leafletOptions(zoomSnap = 0.25)) |>
      addTiles() |>
      addPolygons(data = poly, opacity = 1, fillOpacity = 1, fillColor = 'red') |>
      mapshot2(file = 'mapshot.png', vwidth = 9256, vheight = 3966)
    
    ## Alternatively use webshot directly
    
    p <- leaflet(options = leafletOptions(zoomSnap = 0.25)) |>
      addTiles() |>
      addPolygons(data = poly, opacity = 1, fillOpacity = 1, fillColor = 'red')
    
    
    temp <- tempfile(fileext = ".html")
    htmlwidgets::saveWidget(p, temp)
    
    webshot2::webshot(url = paste0("file://", temp), 
                      file = "webshot.png",
                      selector = ".leaflet",
                      vwidth = 9256,
                      vheight = 3966)
      
    unlink(temp)
    

    res