leafgl icon indicating copy to clipboard operation
leafgl copied to clipboard

Possible memory leak ?

Open vlarmet opened this issue 3 years ago • 9 comments

Hi,

First of all, thank you for this great package ! I want to use leafgl to display a large shapefile (~35000 polygons) in Shiny. The user can choose a variable to map and the map is updated through leafletProxy. However, I notice that memory usage increase each time the user choose a new variable, here is a reproductible example.

library(shiny)
library(leaflet)
library(leafgl)
library(raster)
library(sf)

# Create 50000 pixels raster
r <- raster(extent(matrix( c(-90, -50, 90,  50), nrow=2)), nrow=500, ncol=100, 
            crs = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0") 

Extent <- extent(r)

# Convert to sf shapefile
shp <- rasterToPolygons(r)
shp <- st_as_sf(shp)

## UI ##########
ui <- fluidPage(
  actionButton("go","go"),
  leafglOutput("map")
)

## SERVER ##########
server <- function(input, output) {
  output$map <- renderLeaflet({
    leaflet()  %>% 
      addTiles() %>% 
      fitBounds(lng1 = Extent[1],lat1 = Extent[3], lng2 = Extent[2], lat2 = Extent[4])
  })
  
  observeEvent(input$go,{
    random <- sample(1:100,50000,replace = T)
    pal = colorQuantile(rev(viridis::viridis(10)), random , n = 10)
    
    leafletProxy("map") %>%
      leafgl::removeGlPolygons(layerId = "polygons") %>%
      leafgl::addGlPolygons(layerId = "polygons",data = shp, color = ~pal(random))
  })
}

shinyApp(ui, server)

Each time I click go button, memory increase by about 100Mo.

vlarmet avatar Oct 09 '20 09:10 vlarmet

I think that problem might come from the upstream repo, which is not fully removing the glify instances. (see this issue) In the browser console you can see that L.glify.instances increases with every click on go.

trafficonese avatar Oct 09 '20 12:10 trafficonese

Thanks for the reply. So there is no way to fix that issue at the moment ?

vlarmet avatar Oct 09 '20 15:10 vlarmet

If those are the only polygons you are plotting, you could remove the instances yourself with shinyjs.

In the ui you need to include useShinyjs() and after the leafletProxy call you can run runjs("L.glify.Shapes.instances.splice(0, 1);"). Maybe that would work as a workaround?

trafficonese avatar Oct 09 '20 16:10 trafficonese

Thanks @trafficonese for chiming in here. I have sent you an invite with write access to this repo. I figure you're contributing so much here, you might as well have write access...

tim-salabim avatar Oct 09 '20 16:10 tim-salabim

Wow thank you very much @trafficonese ! It work like a charm.

edit : here is the solution

library(shiny)
library(leaflet)
library(leafgl)
library(raster)
library(sf)
library(shinyjs)
library(jsonify)
library(geojsonsf)

# Create 50000 pixels raster
r <- raster(extent(matrix( c(-90, -50, 90,  50), nrow=2)), nrow=500, ncol=100, 
            crs = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0") 

Extent <- extent(r)

# Convert to sf shapefile
shp <- rasterToPolygons(r)
shp <- st_as_sf(shp)

## UI ##########
ui <- fluidPage(
  useShinyjs(),
  actionButton("go","go"),
  leafglOutput("map")
)

## SERVER ##########
server <- function(input, output) {
  output$map <- renderLeaflet({
    leaflet()  %>% 
      addTiles() %>% 
      fitBounds(lng1 = Extent[1],lat1 = Extent[3], lng2 = Extent[2], lat2 = Extent[4])
  })
  
  observeEvent(input$go,{
    random <- sample(1:100,50000,replace = T)
    pal = colorQuantile(rev(viridis::viridis(10)), random , n = 10)
    
    runjs("L.glify.Shapes.instances.splice(0, 1);")

    leafletProxy("map") %>%
      leafgl::removeGlPolygons(layerId = "polygons") %>%
      leafgl::addGlPolygons(layerId = "polygons",data = shp, color = ~pal(random))
  })
}

shinyApp(ui, server)

vlarmet avatar Oct 09 '20 20:10 vlarmet

I would keep this issue open as this is more of a dirty hack than a proper fix. If multiple layers are included and the one to be removed is not the first in the array, the solution will not work. So, I think this should actually be handled in the underlying library.

Thanks @tim-salabim, I really appreciate it!

trafficonese avatar Oct 12 '20 06:10 trafficonese

Hey all, I'm coming up against this issue using addGlPolylines() and varSelectInput() - If Ichange the column input to the polylines more than ~3 times the app runs out of memory. I have tried the suggested workaround here along with the following (and some variations around it):

runjs("L.glify.Lines.instances.splice(0, 1);")

Neither seem to solve the problem - just wondering if there is an updated workaround or alternative that I can consider? many thanks, Hugh

h-a-graham avatar Oct 06 '21 09:10 h-a-graham