2
votes

I'm creating a Leaflet Shiny app that has several different interactive elements and I'm having some problems getting everything to gel together correctly.

I've created some heavily trimmed example code that (hopefully) captures the extent of my problem:

library(raster)
library(RColorBrewer)
library(leaflet)
library(shiny)

#load rwa shapefile, remove extra columns 
rwa <- getData("GADM", country = "RWA", level = 1)
rwa <- rwa[, -(6:13)]

#load bdi shapefile, remove extra columns, prepare to merge with rwa 
bdi <- getData("GADM", country = "BDI", level = 1)
bdi <- bdi[, -(6:13)]
bdi <- spChFIDs(bdi, paste("bdi", row.names(bdi), sep = "."))

#merge
z <- rbind(rwa, bdi)

#add dummy data
z@data$data1 <- sample(1:20, size = length(z@data$OBJECTID), replace = T)
z@data$data2 <- sample(1:20, size = length(z@data$OBJECTID), replace = T)
z@data$data3 <- sample(1:20, size = length(z@data$OBJECTID), replace = T)
z@data$data4 <- sample(1:20, size = length(z@data$OBJECTID), replace = T)



#define color palettes 
colorPal <- brewer.pal(4, "RdYlGn")
pal <- colorBin(palette = colorPal, domain = 1:20, bins = c(0, 5, 10, 15, 20), pretty = T)

#run shiny app
shinyApp(
  ui = fluidPage(

    #country dropdown selection
    selectInput(inputId = "country", 
                label = "Country", 
                choices = c("", z@data$NAME_0), 
                selected = ""), 

    #test slider
    sliderInput(inputId = "test",
                             label = "Test Slider",
                             min = 1,
                             max = 4,
                             value = 0,
                             step = 1, 
                             animate = T), #end slider

    #map image
    leafletOutput('myMap')#end output, c6, fluidrow
  ), #END UI



  #world level default map output
  server <- function(input, output, session) {

    #initial map rendering (blank tiles)
    output$myMap <- renderLeaflet({
      leaflet() %>%
        addProviderTiles("CartoDB.PositronNoLabels", 
                         options = tileOptions(noWrap = T)) %>% 
        setView(lng = 29.85, lat = -2.7, zoom = 7)
    }) #END LEAFLET OUTPUT 


    #observe slider to update map
    observe({

      #define input as variable
      x <- input$test

      #create object for input country
      dd <- input$country

      #subset regions polygon by the selected country 
      selected <- z[z@data$NAME_0 == dd, ]

      #define proxy map object for dynamic updating
      proxy <- leafletProxy("myMap")

      #create test layerId for polygon removal
      test <- selected@data$OBJECTID

      if(dd != ""){

        #use proxy object so that tiles don't update with each slider input change
        proxy %>% 
          # clearShapes() %>%
          addPolygons(data = selected,
                              fillColor = ~pal(selected@data[[x + 5]]), #add 5 to match column number
                              fillOpacity = 1,
                              weight = 1,
                              stroke = T, 
                              layerId = test)
      } #END CONDITIONAL
    }) #END OBSERVE EVENT
  } #END SERVER
) #END SHINYAPP

There are several different things going on. First, you select your country of interest from the dropdown selection menu (it's important to keep the initial map output as blank tiles with no polygons drawn--shapefiles are only drawn upon country selection from the dropdown menu). From there, you can use the slider to update the choropleth map's color scheme.

I'm using leafletProxy to dynamically update the map so that it is not redrawn every time the user enables some kind of interactivity. This is particularly important when using the slider to update the polygon styles--if the map is redrawn each time, it's very choppy and unsightly. When I run the code as it is written above, the result (in short video form) is here. As you can see, the colors change seamlessly.

With this code, however, I'm not sure how to remove map elements (countries) that have previously been drawn. As you can see in the above linked example, when I click on a second country, the first selected country is not removed. I realize that this is because I'm using leafletProxy to update the map. I am familiar with using layerIds and removeShape() to remove polygons, but with the way that my code is written, I can't get it to work. I've tried employing the layerIds in several different ways with zero luck.

I am able to remove previously selected polygons when I uncomment the line clearShapes() %>% after the proxy call. Unfortunately, this results in the map redrawing every time the slider is updated, resulting in really ugly choppiness.

Is there any way that I can use leafletProxy to both remove previously selected polygons AND seamlessly update the map style with the slider?

1

1 Answers

0
votes

I would split your observe() up and use observeEvent() for each of the inputs. Use clearShapes() in the obervseEvent for input$country but not for input$test.