闪亮的传单不适用于动态菜单

Posted

技术标签:

【中文标题】闪亮的传单不适用于动态菜单【英文标题】:Shiny leaflet doesn't work properly with dynamic menu 【发布时间】:2018-05-29 14:55:41 【问题描述】:

我一直在努力解决这个问题一段时间,但没有成功。我想用表格和地图构建一个闪亮的交互式应用程序。它结合了不同的数据集。这个想法是能够选择所需的数据集并过滤该数据集中的数据并将它们呈现在地图上。

我设法构建了一个交互式菜单和反应式过滤功能,但是传单地图有问题。当我在一些数据集之间切换时,它会崩溃。我只能在 localisations-macro 或 localisations-micro 之间切换,但是当我在 micro 和 macro 之间切换时它不会起作用(见下图)。

问题与错误有关: Warning: Error in sum: invalid 'type' (list) of argument 不过我现在知道如何解决这个问题了。

我也在observerEvent 中尝试过类似的操作 :

if(inpud$data.type=="Localisations")..... else if (input$data.type=="Micro")..... else

但也不行。

以下是应用示例:

library(shiny)
library(leaflet)
library(dplyr)


#### UI
ui <- fluidPage(

  titlePanel("Map"),

  leafletOutput("map"),

  fluidRow(
    column(2, offset = 0, style='padding:10px;',
           radioButtons("data.type", "Type of  data", c("Localisations", "Micro", "Macro"))),
    column(2, offset = 0, style='padding:10px;',
           uiOutput("position")),
    column(2, offset = 0, style='padding:10px;',
           uiOutput("kind"))
  ),

  dataTableOutput("table") ## to check the filtering

)


server <- function(input, output, session) 

  ##### data #####


  sites <- data.frame(Site=c("Site1", "Site2"),
                      Lat=c(54, 56), 
                      Long=c(16, 18)) 


  micro <- data.frame(Site=c(rep("Site1",4), rep("Site2", 4)),
                      Position=c("Micro_pos1","Micro_pos1", "Micro_pos2", "Micro_pos2"),
                      Kind=rep(c("blue_fiber", "red_fiber"), 4),
                      Amount=c(5, 46, 64, 32, 54, 38, 29, 31) )  

  micro <- full_join(micro, sites)

  macro <- data.frame(Site=c(rep("Site1",4), rep("Site2", 4)),
                      Position=c("Macro_pos1","Macro_pos1", "Macro_pos2", "Macro_pos2"),
                      Kind=rep(c("Cigarretes", "Pellets"), 4),
                      Amount=c(3, 16, 4, 12, 14, 18, 19, 21) )  

  macro <- full_join(macro, sites)    


  #### dynamic menu ####

  ### position
  output$position <- renderUI(


    switch(input$data.type,
           "Micro"=radioButtons("position", "Micro position:", 
                                                       choices = c("Micro_pos1", "Micro_pos2")),
           "Macro"=radioButtons("position", "Macro position:", 
                                               choices = c("Macro_pos1", "Macro_pos2"))
    )
  )

  ## kind

  output$kind <- renderUI(


    switch(input$data.type,
           "Micro"=checkboxGroupInput("kind", "kind of micro:", 
                                                             choices = c("blue_fiber", "red_fiber"),
                                                             selected = c("blue_fiber", "red_fiber")),
           "Macro"=checkboxGroupInput("kind", "kind of macro:", 
                                                     choices = c("Cigarretes", "Pellets"),
                                                     selected=c("Cigarretes", "Pellets"))
    )
  )


#### reactive table to filter data to map ####

table <- reactive(


    if(input$data.type=="Localisations")

      return(sites)
      

    else if (input$data.type=="Micro") 

      if (is.null(input$position))
        return(NULL)
      if (!is.null(input$position))

        micro <-micro[micro$Position==input$position,]
      micro<-micro[micro$Kind %in% input$kind,]

      micro <- micro %>% 
        group_by(Site, Lat, Long, Position)%>%
        summarise(Amount=sum(Amount))
      micro
      

  else if (input$data.type=="Macro") 

    if (is.null(input$position))
      return(NULL)
    if (!is.null(input$position))

      macro <-macro[macro$Position==input$position,]
    macro<-macro[macro$Kind %in% input$kind,]

     macro <- macro %>%
      group_by(Site, Lat, Long, Position)%>%
      summarise(Amount=sum(Amount))
    macro
    
  )


#### table with filtered data ####
output$table <- renderDataTable(
  table()
)



#### base map ####


  output$map <- renderLeaflet(
    leaflet(sites) %>%
      setView(lat=55, lng=17, zoom=6) %>%
      addProviderTiles(providers$Esri.WorldImagery) %>%
      addCircleMarkers(lng=~Long, lat=~Lat, label=~Site, 
                       labelOptions = labelOptions(noHide =T))
  )




  ##### and now it gets complicated :( ####

   observeEvent( c( input$data.type, input$position, input$kind), 

    if(input$data.type=="Localisations")

      leafletProxy("map", data=sites) %>%
        clearMarkers() %>%
        clearShapes()%>%
        addCircleMarkers(lng=~Long, lat=~Lat, label=~Site,
                         labelOptions = labelOptions(noHide =T), fillColor = "red")
    


     else 

        if (is.null(input$position))
           return(NULL)
        if (is.null(input$kind))
          return(NULL)


        leafletProxy("map", data=table()) %>%
        clearMarkers() %>%
        clearShapes()%>%

        addCircles(lng=~Long, lat=~Lat, label=~Site, color="white", fill="white",
                   labelOptions = labelOptions(noHide =T),
                   radius = ~Amount*1000) %>%
        addLabelOnlyMarkers(lng=~Long, lat=~Lat, label=~as.character(Amount),
                            labelOptions = labelOptions(noHide = T, direction = 'top', textOnly = T, textsize="20px"))

     



  )

  ### server end


# Run the application 
shinyApp(ui = ui, server = server)

【问题讨论】:

【参考方案1】:

问题在于时机:

当您从微切换到宏时,input$kind 仍设置为仅适用于微的值 然后调用 table() 来创建地图。它过滤 macro[macro$Kind %in% input$kind,],这将导致一个空集,因为 input$kind 仍然包含红色和蓝色纤维,而不是“Cigarretes”和“Pellets”

您使用

更新选择框
output$kind <- renderUI(


switch(input$data.type,
       "Micro"=checkboxGroupInput("kind", "kind of micro:", 
                                  choices = c("blue_fiber", "red_fiber"),
                                  selected = c("blue_fiber", "red_fiber")),
       "Macro"=checkboxGroupInput("kind", "kind of macro:", 
                                  choices = c("Cigarretes", "Pellets"),
                                  selected=c("Cigarretes", "Pellets"))
)
)

但这仅在调用 table() 后对 input$kind 产生影响。

我想到的第一个解决方案是通过提交按钮而不是通过对 input$kind、input$data.type 和 input$position 的每次更改来触发对 table() 的调用。所以 table() 将成为一个非反应性函数,并且创建地图的观察者observeEvent( c( input$data.type, input$position, input$kind), ...) 发生了变化:

所以包括一个操作按钮: ui

     leafletOutput("map"),

     fluidRow(
       column(2, offset = 0, style='padding:10px;',
              radioButtons("data.type", "Type of  data", c("Localisations", 
     "Micro", "Macro"))),
        column(2, offset = 0, style='padding:10px;',
             uiOutput("position")),
       column(2, offset = 0, style='padding:10px;',
             uiOutput("kind"))
     ),
     actionButton("button", "submit"),
     dataTableOutput("table") ## to check the filtering      
    )

然后改表函数:

  table <- function()


    if(input$data.type=="Localisations")

      return(sites)
      

    else if (input$data.type=="Micro") 

      if (is.null(input$position))
        return(NULL)
      if (!is.null(input$position))

        micro <-micro[micro$Position==input$position,]
      micro<-micro[micro$Kind %in% input$kind,]

      micro <- micro %>% 
        group_by(Site, Lat, Long, Position)%>%
        summarise(Amount=sum(Amount))
      micro
    

    else if (input$data.type=="Macro") 

      if (is.null(input$position))
        return(NULL)
      if (!is.null(input$position))

        macro <-macro[macro$Position==input$position,]
      macro<-macro[macro$Kind %in% input$kind,]

      macro <- macro %>%
        group_by(Site, Lat, Long, Position)%>%
        summarise(Amount=sum(Amount))
      macro
    
  

最后调整观察者:

  observeEvent( input$button, 

    if(input$data.type=="Localisations")

      leafletProxy("map", data=sites) %>%
        clearMarkers() %>%
        clearShapes()%>%
        addCircleMarkers(lng=~Long, lat=~Lat, label=~Site,
                         labelOptions = labelOptions(noHide =T), fillColor = "red")
    


    else 

      if (is.null(input$position))
        return(NULL)
      if (is.null(input$kind))
        return(NULL)


      leafletProxy("map", data=table()) %>%
        clearMarkers() %>%
        clearShapes()%>%

        addCircles(lng=~Long, lat=~Lat, label=~Site, color="white", fill="white",
                   labelOptions = labelOptions(noHide =T),
                   radius = ~Amount*1000) %>%
        addLabelOnlyMarkers(lng=~Long, lat=~Lat, label=~as.character(Amount),
                            labelOptions = labelOptions(noHide = T, direction = 'top', textOnly = T, textsize="20px"))

    
  )

要使您的数据表具有响应性,您可以这样做:

  output$table <- renderDataTable(
    input$button
    table()
  )

【讨论】:

【参考方案2】:

非常感谢@ge.org 的努力,但是带有登顶按钮的解决方案不适合我的应用程序,因为在实际应用程序中我有更多的类别,并且用户每次登顶更改都会很尴尬。但是由于您对时间的评论,我设法通过 edditig observeEvent 绕过了这一点。我为每个 data.type 将其拆分为三个单独的条件,而不是在负责 micromacro 的条件中,我添加了新条件以省略错误来自input$kind 的类别。这会在控制台中发出一些警告,但似乎不会影响整个应用程序。

这是新代码:

library(shiny)
library(leaflet)
library(dplyr)



#### UI
ui <- fluidPage(

  titlePanel("Map"),

  leafletOutput("map"),

  fluidRow(
    column(2, offset = 0, style='padding:10px;',
           radioButtons("data.type", "Type of  data", c("Localisations", "Micro", "Macro"))),
    column(2, offset = 0, style='padding:10px;',
           uiOutput("position")),
    column(2, offset = 0, style='padding:10px;',
           uiOutput("kind"))
  ),

  dataTableOutput("table"), ## to check the filtering
  textOutput("kind.of")
)


server <- function(input, output, session) 

  ##### data #####


  sites <- data.frame(Site=c("Site1", "Site2"),
                      Lat=c(54, 56), 
                      Long=c(16, 18)) 


  micro <- data.frame(Site=c(rep("Site1",4), rep("Site2", 4)),
                      Position=c("Micro_pos1","Micro_pos1", "Micro_pos2", "Micro_pos2"),
                      Kind=rep(c("blue_fiber", "red_fiber"), 4),
                      Amount=c(5, 46, 64, 32, 54, 38, 29, 31) )  

  micro <- full_join(micro, sites)

  macro <- data.frame(Site=c(rep("Site1",4), rep("Site2", 4)),
                      Position=c("Macro_pos1","Macro_pos1", "Macro_pos2", "Macro_pos2"),
                      Kind=rep(c("Cigarretes", "Pellets"), 4),
                      Amount=c(3, 16, 4, 12, 14, 18, 19, 21) )  

  macro <- full_join(macro, sites)    


  #### dynamic menu ####

  ### position
  output$position <- renderUI(


    switch(input$data.type,
           "Micro"=radioButtons("position", "Micro position:", 
                                                       choices = c("Micro_pos1", "Micro_pos2")),
           "Macro"=radioButtons("position", "Macro position:", 
                                               choices = c("Macro_pos1", "Macro_pos2"))
    )
  )

  ## kind

  output$kind <- renderUI(


    switch(input$data.type,
           "Micro"=checkboxGroupInput("kind", "kind of micro:", 
                                                             choices = c("blue_fiber", "red_fiber"),
                                                             selected = c("blue_fiber", "red_fiber")),
           "Macro"=checkboxGroupInput("kind", "kind of macro:", 
                                                     choices = c("Cigarretes", "Pellets"),
                                                     selected=c("Cigarretes", "Pellets"))
    )
  )


#### reactive table to filter data to map ####

table <- reactive(


    if(input$data.type=="Localisations")

      return(sites)
      

    else if (input$data.type=="Micro") 

      if (is.null(input$position))
        return(NULL)
      if (!is.null(input$position))

        micro <-micro[micro$Position==input$position,]
      micro<-micro[micro$Kind %in% input$kind,]

      micro <- micro %>% 
        group_by(Site, Lat, Long, Position)%>%
        summarise(Amount=sum(Amount))
      micro
      

  else if (input$data.type=="Macro") 

    if (is.null(input$position))
      return(NULL)
    if (!is.null(input$position))

      macro <-macro[macro$Position==input$position,]
    macro<-macro[macro$Kind %in% input$kind,]

     macro <- macro %>%
      group_by(Site, Lat, Long, Position)%>%
      summarise(Amount=sum(Amount))
    macro
    
  )


#### table with filtered data ####
output$table <- renderDataTable(
  table()
)


#### base map ####


  output$map <- renderLeaflet(
    leaflet(sites) %>%
      setView(lat=55, lng=17, zoom=6) %>%
      addProviderTiles(providers$Esri.WorldImagery) %>%
      addCircleMarkers(lng=~Long, lat=~Lat, label=~Site, 
                       labelOptions = labelOptions(noHide =T))
  )


  #### updating the map #####

   observeEvent( c( input$data.type, input$position, input$kind), 



  if(input$data.type=="Localisations")

      leafletProxy("map", data=sites) %>%
        clearMarkers() %>%
        clearShapes()%>%
        addCircleMarkers(lng=~Long, lat=~Lat, label=~Site,
                         labelOptions = labelOptions(noHide =T), fillColor = "red")
    


     else if (input$data.type=="Micro") 

        if (is.null(input$position))
           return(NULL)
         if (is.null(input$kind))
          return(NULL)

###########################################################
  ####### and here 4 new line that did all the job #########
       if (input$position %in% c("Macro_pos1", "Macro_pos2")) 
         return(NULL) ## new line
       if (input$kind %in% c("Cigarretes", "Pellets"))
         return(NULL)
  #################################################### 



        leafletProxy("map", data=table()) %>%
        clearMarkers() %>%
        clearShapes()%>%

        addCircles(lng=~Long, lat=~Lat, label=~Site, color="white", fill="white",
                   labelOptions = labelOptions(noHide =T),
                   radius = ~Amount*1000) %>%
        addLabelOnlyMarkers(lng=~Long, lat=~Lat, label=~as.character(Amount),
                            labelOptions = labelOptions(noHide = T, direction = 'top', textOnly = T, textsize="20px"))

           

     else if(input$data.type=="Macro") 

       if (is.null(input$position))
         return(NULL)
       if (is.null(input$kind))
         return(NULL)

###########################################################
  ####### and here 4 new line that did all the job #########
       if (input$position %in% c("Micro_pos1", "Micro_pos2"))
         return(NULL)
       if (input$kind %in% c("blue_fiber", "red_fiber"))
         return(NULL)
  ##############################################

       leafletProxy("map", data=table()) %>%
         clearMarkers() %>%
         clearShapes()%>%

         addCircles(lng=~Long, lat=~Lat, label=~Site, color="white", fill="white",
                    labelOptions = labelOptions(noHide =T),
                    radius = ~Amount*1000) %>%
         addLabelOnlyMarkers(lng=~Long, lat=~Lat, label=~as.character(Amount),
                             labelOptions = labelOptions(noHide = T, direction = 'top', textOnly = T, textsize="20px"))

     

      )

  ### server end


# Run the application 
shinyApp(ui = ui, server = server)

【讨论】:

以上是关于闪亮的传单不适用于动态菜单的主要内容,如果未能解决你的问题,请参考以下文章

Sumoselect 插件不适用于动态选择下拉菜单

选项卡面板的动态高度调整不适用于信息框

用于动态选择菜单的 AlpineJS

r - 空 textInput() 导致传单闪亮应用程序出错

Angular 5中json对象的动态嵌套材质菜单

如何在闪亮的 R 应用程序中使用传单添加控制输入?