4

I'm trying to show two synced maps on a shiny app that show densities of species in two different samples based on inputs species and year. I have it working using leafsync, but not set up to use leaflet proxies, so every time I change an input, the maps completely reset (i.e. lose positioning and zoom). I want to be able to change species and year without losing my place on the map.

I know that I need to use leafletProxy to add raster images on top of leaflet maps without resetting, but I am unsure how to do this using leafsync (or otherwise, this may be a job for mapview or tmap), since the two synced maps share one name. Here's what I've tried:

working version NOT using leafletProxy:

options("rgdal_show_exportToProj4_warnings"="none") # mute warnings from rgdal because I'm using proj strings
library(shiny)
library(shinyWidgets)
library(leaflet)
library(raster)
library(leafsync)
library(shinydashboard)


set.seed(1)
frog <- data.frame(x=sample(seq(from=-105, to=-95,by=.4), 300, replace = T),
                   y = sample(seq(from=35, to=45,by=.4), 300, replace=T),
                   sample1.2000 = runif(300,min=40, max = 250),
                   sample2.2000 = runif(300,min=40, max = 250),
                   sample1.2001 = runif(300,min=10, max = 220),
                   sample2.2001 = runif(300,min=10, max = 220),
                   sample1.2002 = runif(300,min=0, max = 200),
                   sample2.2002 = runif(300,min=0, max = 200)
                   )
toad <- data.frame(x=sample(seq(from=-105, to=-95,by=.4), 500, replace = T),
                   y = sample(seq(from=35, to=45,by=.4), 500, replace=T),
                   sample1.2000 = runif(500,min=100, max = 750),
                   sample2.2000 = runif(500,min=100, max = 750),
                   sample1.2001 = runif(500,min=500, max = 900),
                   sample2.2001 = runif(500,min=100, max = 600),
                   sample1.2002 = runif(500,min=300, max = 900),
                   sample2.2002 = runif(500,min=50, max = 600)
                   )

 ui <- 
   fluidPage(
     fluidRow(
       box(width = 12,
       box(width=6,
           radioGroupButtons(
             inputId = "species",
             label = "Target Species",
             choiceNames  = list("Frog","Toad"),
             choiceValues = list("frog","toad"),
             selected = "frog",
             justified = TRUE,
             status="primary"
           ),
       ),
       box(width=6,
           sliderInput("year", 
                       label = "Year", min = 2000, 
                       max = 2002, value = 2000,
                       sep="")
       ),
     ),
     ),
     fluidRow( 
           uiOutput('map', height = "150vh") 
     )
   )
 

server <- function(input, output) {
  
  # set limits for color scale, dependent on species
  spp_lim <- eventReactive(input$species, {
    switch(input$species,
           "frog" = c(0:250), # highest frog density is 250
           "toad" = c(0:1000), # highest toad density is 1000
    )
  })
  
  # create a color palette for the map
  map_pal <- reactiveValues() 
  
  observe({
          map_pal$pal <- colorNumeric(palette = "plasma", 
                                  spp_lim(),
                                  na.color = "transparent",
                                  reverse=F)
  })
  
  # make map using renderUI and leafsync
  output$map <- renderUI({


    # pull correct columns for correct species
    map_dat <- get(input$species) %>%
      dplyr::select(x,y,
                    paste0("sample1.",input$year),
                    paste0("sample2.",input$year)
                    ) 
     
    
    # rasterize
    raster_1 <- rasterFromXYZ(map_dat[,c(1,2,3)],
                              crs = "+init=epsg:4326 +proj=longlat +ellps=WGS84 " )
    raster_2 <- rasterFromXYZ(map_dat[,c(1,2,4)],
                              crs = "+init=epsg:4326 +proj=longlat +ellps=WGS84 ")
    
    
    # start leafsync
    sync(
      
      # map 1 -------------
      leaflet( options = leafletOptions(minZoom = 3, maxZoom = 7, zoomControl = TRUE)) %>%
        addProviderTiles("CartoDB.VoyagerNoLabels") %>%
        addRasterImage(raster_1, opacity = 0.7, colors =  map_pal$pal,
                       project=TRUE) %>%
        addLegend(position = "bottomright",
                  pal = map_pal$pal, 
                  values = spp_lim(),
                  title = paste0(stringr::str_to_title(input$species)," density"),
                  opacity = 1
                  ) %>%
        addControl("<b>Sample 1</b>", position = "topright"),
      
      
      # map 2 ------------
      leaflet( options = leafletOptions(minZoom = 3, maxZoom = 7, zoomControl = TRUE)) %>%
        addProviderTiles("CartoDB.VoyagerNoLabels") %>%
        addRasterImage(raster_2, opacity = 0.7, colors =  map_pal$pal,
                       project=TRUE) %>%
        addLegend(position = "bottomright",
                  pal = map_pal$pal, 
                  values = spp_lim(),
                  title = paste0(stringr::str_to_title(input$species)," density"),
                  opacity = 1
        ) %>%       
        addControl("<b>Sample 2</b>", position = "topright")
    )
    
  }) # end render map
  
}


shinyApp(ui = ui, server = server)

And non working version set up to use leafletProxy:

options("rgdal_show_exportToProj4_warnings"="none") # mute warnings from rgdal because I'm using proj strings
library(shiny)
library(shinyWidgets)
library(leaflet)
library(raster)
library(leafsync)
library(shinydashboard)


set.seed(1)
frog <- data.frame(x=sample(seq(from=-105, to=-95,by=.4), 300, replace = T),
                   y = sample(seq(from=35, to=45,by=.4), 300, replace=T),
                   sample1.2000 = runif(300,min=40, max = 250),
                   sample2.2000 = runif(300,min=40, max = 250),
                   sample1.2001 = runif(300,min=10, max = 220),
                   sample2.2001 = runif(300,min=10, max = 220),
                   sample1.2002 = runif(300,min=0, max = 200),
                   sample2.2002 = runif(300,min=0, max = 200)
)
toad <- data.frame(x=sample(seq(from=-105, to=-95,by=.4), 500, replace = T),
                   y = sample(seq(from=35, to=45,by=.4), 500, replace=T),
                   sample1.2000 = runif(500,min=100, max = 750),
                   sample2.2000 = runif(500,min=100, max = 750),
                   sample1.2001 = runif(500,min=500, max = 900),
                   sample2.2001 = runif(500,min=100, max = 600),
                   sample1.2002 = runif(500,min=300, max = 900),
                   sample2.2002 = runif(500,min=50, max = 600)
)



ui <- 
  fluidPage(
    fluidRow(
      box(width = 12,
          box(width=6,
              radioGroupButtons(
                inputId = "species",
                label = "Target Species",
                choiceNames  = list("Frog", 
                                    "Toad"),
                choiceValues = list("frog","toad"),
                selected = "frog",
                justified = TRUE,
                status="primary"
              ),
          ),
          box(width=6,
              sliderInput("year", 
                          label = "Year", min = 2000, 
                          max = 2002, value = 2000,
                          sep="")
          ),
      ),
    ),
    fluidRow( 
      uiOutput('map', height = "150vh") 
    )
  )


server <- function(input, output) {
  
  # set limits for scales, dependent on species
  spp_lim <- eventReactive(input$species, {
    switch(input$species,
           "frog" = c(0:250), # highest frog density is 250
           "toad" = c(0:1000), # highest toad density is 1000
    )
  })
  
  # create a color palette for the map
  map_pal <- reactiveValues() 
  
  observe({
    map_pal$pal <- colorNumeric(palette = "plasma", 
                                spp_lim(),
                                na.color = "transparent",
                                reverse=F)
  })
  
  
  
  
  output$map <- renderUI({

    # add blank leaflet map 
    sync(
      leaflet( options = leafletOptions(minZoom = 3, maxZoom = 7, zoomControl = TRUE)) %>%
        addProviderTiles("CartoDB.VoyagerNoLabels") %>%
        setView(lng = -100, lat = 40, zoom = 5),
      
      leaflet( options = leafletOptions(minZoom = 3, maxZoom = 7, zoomControl = TRUE)) %>%
        addProviderTiles("CartoDB.VoyagerNoLabels") %>%
        setView(lng = -100, lat = 40, zoom = 5) 
      )
    
    
  }) # end render map
  

# observe term for adding rasters
  observe({
    
    
    # get data
    map_dat <- get(input$species) %>%
      dplyr::select(x,y, 
                    paste0("sample1.",input$year),
                    paste0("sample2.",input$year)) 
    
    # rasterize
    raster_1 <- rasterFromXYZ(map_dat[,c(1,2,3)],
                              crs = "+init=epsg:4326 +proj=longlat +ellps=WGS84 " )
    raster_2 <- rasterFromXYZ(map_dat[,c(1,2,4)],
                              crs = "+init=epsg:4326 +proj=longlat +ellps=WGS84 ")
    
    # set palette and data for raster object
    pal <-   map_pal$pal
    

    # NOTE: this next line needs a name specified, but I don't know how to specify 
    # because "map" is the entire sync object, not the individual maps that are being synced, 
    # so I am not accurately telling Shiny which map to add which raster image to.
    leafletProxy("map") %>%
      clearImages() %>% 
      addRasterImage(raster_1, colors = pal, opacity = 0.7,
                     project=TRUE)
    
    
    leafletProxy("map") %>%
      clearImages() %>% 
      addRasterImage(raster_2, colors = pal, opacity = 0.7,
                     project=TRUE)
    
  })
  
}


shinyApp(ui = ui, server = server)

Thanks!

Jake L
  • 987
  • 9
  • 21
  • The problem is that leafletProxy doesn't work with renderUI. It wants leafletOutput() to be called from within the UI rather than the Server. The solution is... well I'm still working on that. – vorpal May 01 '22 at 07:39
  • @Jake L Have you found a solution ? – gdevaux Dec 20 '22 at 14:46

0 Answers0