Shape file maps with Leaflet

Hi everybody,

I'm using Leaflet with a shape file of all Brazil municipality (5570) but when is rendering is take a lot.
Any suggest to use a different packages or to simplify a shape file??

my code is:

Preformatted text
map_mun = brmap::brmap_municipio_simples
map_mun = map_mun %>% mutate(municipio_cod = as.character(municipio_cod) )
simplified_shape_mun <- ms_simplify(map_mun,keep = 0.01)

Thanks in advance.

1 Like

I had a similar problem. I found that simplifying didn't help much.

Have you tried starting the map zoomed in? I think leaflet only renders polygons that are in the window.

Also you can try adding the data gradually, using leafletProxy. It takes just as long but might seem more acceptable to the user.

mapview is often recommended but I haven't tried it.

Also this book
https://bookdown.org/robinlovelace/geocompr/adv-map.html#interactive-maps

1 Like

Thanks a lot for your reply, i already know this book really helpfull.
I really prefer leaflet, i think it has more features and i already know hot to use it.

The only feature that i never used is leafletProxy, seems to save my life because is not rendering every time the map.

Can you help me to implement??

I always use leafletProxy, it's pretty easy.

https://forum.posit.co/t/speed-up-leaflet-redraw-in-shiny/65561

1 Like

I try to implement in my code follow your link but i've got this error and i don't know why.

This is my code to render the image.

''' r
mortalidade = criar_mapa(df_mapa,"Tx_weight","UF")
output$plot_mapa_mortalidade <- renderLeaflet({
mortalidade
})

quintiles = quantile(df_mapa$Tx_weight, probs = (0:5)/5)
pal_mortalidade <- colorBin("YlOrRd", domain = df_mapa$Tx_weight, bins = quintiles)

observeEvent(df_mapa, {
  leafletProxy("plot_mapa_mortalidade", data = df_mapa) %>%
    setShapeStyle(layerId = ~estado_cod, fillColor=~pal_mortalidade$Tx_weight, color = white) 
})

'''

And i've got this error

Warning: Error in setShapeStyle: could not find function "setShapeStyle"

renderLeaflet({}) has to return a leaflet

setShapeStyle is not yet an R leaflet function (https://github.com/rstudio/leaflet/issues/496)

remove the ~ from fillColor=pal_mortalidade$Tx_weight

color = "white"

I have good experience with GADM data; the shapefiles are decent and reliable. On the other hand Brazil is big, and ~5500 MunicĂ­pios is a lot (for some reason I have counted 5504, not 5570 - but the problem is the same).

Rendering them all takes me around 7.5 seconds on a mid range piece of hardware, not counting the download time.

library(leaflet)
library(tictoc)
library(sf)

borders <- readRDS(url("https://biogeo.ucdavis.edu/data/gadm3.6/Rsf/gadm36_BRA_2_sf.rds"))

tictoc::tic()

leaflet(data = borders) %>% 
  addProviderTiles("Stamen.Toner") %>% 
  addPolygons(fill = NA,
              color = "red")

tictoc::toc()
7.599 sec elapsed
1 Like

Thanks a lot for your answer,

I post here my code because I would like to try to implement leafletProxy. I really want to know how to use it.
I have different filter to apply on my map.

Here the peace of code that I have and I try to implement leaflet can you help me??

  quintiles = unique(quantile(df_mapa$Tx_weight, probs = (0:5)/5,na.rm = T)) 
  pal_mortalidade <- colorBin("YlOrRd", domain = df_mapa$Tx_weight, bins = quintiles)

  labels <- sprintf(
    "<strong>%s</strong><br/>%g Taxa  </sup>",
    df_mapa$UF,df_mapa$Tx_weight) %>% lapply(htmltools::HTML)
  
  mortalidade  <- leaflet(df_mapa, options = leafletOptions(minZoom = 3,zoomSnap = 0.1,zoomDelta = 
    0.1) ) %>%  addTiles() %>% addPolygons(
    fillColor = ~pal_mortalidade(df_mapa$Tx_weight),
    weight = 0.5,
    opacity = 1,
    color = "white",
    dashArray = "3",
    fillOpacity = 0.7,
    label = labels,
    layerId = ~UF,
    highlightOptions = highlightOptions(color = "black", weight = 2,
                                        bringToFront = TRUE)) %>%
    addResetMapButton() %>% addLegend(pal = pal_mortalidade, values = ~variavel, opacity = 0.7, 
    title = NULL,position = "bottomright")%>%
    onRender(
      "function(el, x) {
                L.easyPrint({
                  sizeModes: ['Current', 'A4Landscape', 'A4Portrait'],
                  filename: 'mymap',
                  exportOnly: true,
                  hideControlContainer: false
                }).addTo(this);
                }"
    )

    output$plot_mapa_mortalidade <- renderLeaflet({
        mortalidade
    })

observeEvent(mortalidade, {
  leafletProxy("plot_mapa_mortalidade", data = mortalidade) %>%
    setShapeStyle(layerId = ~estado_cod, fillColor=~pal_mortalidade$Tx_weight, color = white) 
})

before this code i Upload in my project the file on the github page:
https://github.com/rstudio/leaflet/issues/496, with the jvascript function.
But still dosn't work,

I'm wondering if there is a solution without using javascript and only leafletprxy.

Thanks in advanceThis text will be hidden

Leaflet proxy is helpful when you have to redraw the same (or almost the same map). I don't think it would help much when the speed of the first render is the issue.

I would rather start by questioning your need for 5500 polygons (it is a lot). Could you plot your data per state? Or, if overview of entire country is necessary, could you possibly replace the polygons by their centroids? Plotting 5500 points is, relatively speaking, a breeze...

library(leaflet)
library(tictoc)
library(sf)

borders <- readRDS(url("https://biogeo.ucdavis.edu/data/gadm3.6/Rsf/gadm36_BRA_2_sf.rds"))

points <- st_centroid(borders)

tictoc::tic()

leaflet(data = points) %>% 
  addProviderTiles("Stamen.Toner") %>% 
  addCircleMarkers(fillColor = "red",
                   stroke = F)

tictoc::toc()
0.076 sec elapsed

The difference between points and polygons in this level of detail seems small, but the speed of rendering improved by several orders of magnitude.

Thanks a lot for your answer jlacko but i nedd to make a cloropeth map lik picture below:

The problem like you say is the first time rendering, is take 2 minutes when i render 4 images but when a want to render 8 plot with different filter it take 15 minutes.

You know if there is something that i can do? please help me.
Thanks in advance

This is a code that i'm using to simplify my shape files: (You know if there is a simple one?)

map_mun = brmap::brmap_municipio_simples
map_mun = map_mun %>% mutate(municipio_cod = as.character(municipio_cod)  )
simplified_shape_mun <- ms_simplify(map_mun,keep = 0.01)

Alberto

I sort of fail to understand the point of plotting 4 plots of 5500 polygons each - if the decision was up to me I would choose a higher level aggregation; states at least.

But from the way your question was raised I understand the decision is not up to you to make; yours is to do or die.

In such case I suggest you simplify your polygons. Rather than sf::st_simplify() which simplifies each polygons separately I would suggest exporting the MunicĂ­pios to a service such as https://mapshaper.org/ in TopoJSON format and processing it as such.

Topojsons are sort of special as that they store boundaries only once, meaning that both sides of the border are simplified in the same manner (without creating slivers); this makes them preferable to ESRI shapefiles or geojsons as far as simplification is concerned.

1 Like

Thanks jlacko,

Yes is not my decision otherwise I wouldn't do such a crazy thing, i also did UF level aggregation and it works fast and fine.

My question now is how can i plot my map with TopoJSON file and merge my dataframe with the real data to TopoJSON and plot it??

I try to use https://mapshaper.org/, the problem is when i export the shape files it comes without any column to merge with my dataframe. You know how i can do??

Thanks n advance

Hi @sirox84,

sorry for the delay; life happened.

On second thought I do not recommend online mapshaper, but the R package {rmapshaper} which is very much the same thing.

By applying rmapshaper::ms_simplify() I was able to reduce the rendering time significantly (to about 1/8 of the original) while maintaining the structure of the shapefile (i.e. retaining the keys for linking your data items).

Still slow, but might make the difference between bearably and unbearably slow...

library(leaflet)
library(tictoc)
library(sf)

borders <- readRDS(url("https://biogeo.ucdavis.edu/data/gadm3.6/Rsf/gadm36_BRA_2_sf.rds"))

# this is the fun part!
simple_borders <- rmapshaper::ms_simplify(borders, keep = 0.01,
                                          keep_shapes = TRUE)

tictoc::tic()

leaflet(data = simple_borders) %>% 
  addProviderTiles("Stamen.Toner") %>% 
  addPolygons(fill = NA,
              color = "red")

tictoc::toc() 
0.896 sec elapsed
1 Like

Jlacko thanks a lot! now it works faster 8 maps in 47 second perfect, my goal was less the 1 minutes.

Thank so much you are lifesaver now, the second time for not waiting 47 second every time, I try to use lafletProxy but without success.

Here is my code can you help me to find the error please??

quintiles = unique(quantile(df_mapa$Tx_weight, probs = (0:5)/5,na.rm = T)) 
  pal_mortalidade <- colorBin("YlOrRd", domain = df_mapa$Tx_weight, bins = quintiles)

  labels <- sprintf(
    "<strong>%s</strong><br/>%g Taxa  </sup>",
    df_mapa$UF,df_mapa$Tx_weight) %>% lapply(htmltools::HTML)
  
  mortalidade  <- leaflet(df_mapa, options = leafletOptions(minZoom = 3,zoomSnap = 0.1,zoomDelta = 
    0.1) ) %>%  addTiles() %>% addPolygons(
    fillColor = ~pal_mortalidade(df_mapa$Tx_weight),
    weight = 0.5,
    opacity = 1,
    color = "white",
    dashArray = "3",
    fillOpacity = 0.7,
    label = labels,
    layerId = ~UF,
    highlightOptions = highlightOptions(color = "black", weight = 2,
                                        bringToFront = TRUE)) %>%
    addResetMapButton() %>% addLegend(pal = pal_mortalidade, values = ~variavel, opacity = 0.7, 
    title = NULL,position = "bottomright")%>%
    onRender(
      "function(el, x) {
                L.easyPrint({
                  sizeModes: ['Current', 'A4Landscape', 'A4Portrait'],
                  filename: 'mymap',
                  exportOnly: true,
                  hideControlContainer: false
                }).addTo(this);
                }"
    )

    output$plot_mapa_mortalidade <- renderLeaflet({
        mortalidade
    })

observeEvent(mortalidade, {
  leafletProxy("plot_mapa_mortalidade", data = mortalidade) %>%
    setShapeStyle(layerId = ~estado_cod, fillColor=~pal_mortalidade$Tx_weight, color = white) 
})

Thnks a lot

I am glad I could help with the simplification of your shapefile.

I am afraid I am not much of a shiny wizard and debugging of your leaflet proxy issue would be over my head :frowning_face:

Thanks anyway I will try to implement the code, not seem to be difficult we can close the post