【问题标题】:Reference to a leaflet map from a golem module to another从 golem 模块到另一个模块的传单映射的引用
【发布时间】:2021-12-30 13:04:48
【问题描述】:

我正在构建一个闪亮的应用程序,使用 Golem 作为框架。 在我的应用程序中,我制作了几个模块,所有模块都通过传单地图链接。 但是,除了创建地图的模块之外,我无法从另一个模块更新地图。

我已尝试考虑here 的建议,将map_idparent_session 包含在模块调用中,但每当我尝试对地图进行任何更改时,应用程序仍然会崩溃(没有错误跟踪)。

这是我的代码的精简版本,在单独的文件中:

app_server.R

#' The application server-side
#' 
#' @param input,output,session Internal parameters for {shiny}. 
#'     DO NOT REMOVE.
#' @import shiny
#' @import leaflet
#' @noRd
app_server <- function( input, output, session ) {
  
  
  r <- reactiveValues(
    map=NULL,
    origin=list(lat=NULL, lng=NULL),
    destination=list(lat=NULL, lng=NULL)
  )
  
  mod_basemap_server("basemap_ui_1", r, session)
  mod_itinerary_server("itinerary_ui_1", r, map_id="basemap", parent_session=session)
}

app_ui.R

#' The application User-Interface
#' 
#' @param request Internal parameter for `{shiny}`. 
#'     DO NOT REMOVE.
#' @import shiny
#' @noRd
app_ui <- function(request) {
  tagList( 
    bootstrapPage(      
      mod_basemap_ui("basemap_ui_1"),
      mod_itinerary_ui("itinerary_ui_1")
      )
    
  )
}

mod_basemap.R

#' basemap UI Function
#'
#' @description A shiny Module.
#'
#' @param id,input,output,session Internal parameters for {shiny}.
#'
#' @noRd 
#'
#' @importFrom shiny NS tagList 
mod_basemap_ui <- function(id){
  ns <- NS(id)
  tagList(
    # Map background
    leaflet::leafletOutput(ns("basemap"))    
  )
}
    
#' basemap Server Functions
#'
#' @noRd 
mod_basemap_server <- function(id, r, session){
  shiny::moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    output$basemap <- leaflet::renderLeaflet({
      # generate base leaflet
      map = leaflet::leaflet(options = leaflet::leafletOptions(zoomControl = FALSE)) %>%
        leaflet::addTiles(leaflet::providers$OpenStreetMap) %>%
        leaflet::addProviderTiles(leaflet::providers$OpenStreetMap,
                                  group="Open Street Map") %>% 
        leaflet::addProviderTiles(leaflet::providers$OpenTopoMap,
                                  group="Open Topo Map") %>% 
        leaflet::addProviderTiles(leaflet::providers$Esri.WorldImagery,
                                  group="Esri World Imagery") %>% 
        leaflet::fitBounds(2.78, 44.85, 3.41, 44.71) %>%
        leaflet::addLayersControl(
          baseGroups = c("Open Street Map", "Open Topo Map", "Esri World Imagery")
        )
      map
    })
        
    # Clicks on the map
    observeEvent(input$basemap_click, {
      click = input$basemap_click
      # Check whether we're updating origin marker or destination marker
      if(r$orig_dest_switch=="orig"){
        r$origin$lat = click$lat
        r$origin$lng = click$lng
        # Add origin marker on the map (after removing previously added origin)
        leaflet::leafletProxy('basemap')%>%
          leaflet::clearGroup("origin") %>%
          leaflet::addMarkers(lng=r$origin$lng, lat=r$origin$lat,
                     group="origin")
      } else {
        r$destination$lat = click$lat
        r$destination$lng = click$lng
        # Add destination marker on the map (after removing previously added destination)
        leaflet::leafletProxy('basemap')%>%
          leaflet::clearGroup("destination") %>%
          leaflet::addMarkers(lng=r$destination$lng, lat=r$destination$lat,
                              group="destination")
      }
    })
    
  })
}

mod_itinerary.R

#' itinerary UI Function
#'
#' @description A shiny Module.
#'
#' @param id,input,output,session Internal parameters for {shiny}.
#'
#' @noRd 
#'
#' @importFrom shiny NS tagList 
mod_itinerary_ui <- function(id){
  ns <- NS(id)
  tagList(
    absolutePanel(id="vehicleDetails", bottom=10, left=15, h4('Itinerary'),
                  h6("First select an origin on the map, then select a destination before plotting"),
                  style='background-color:white; opacity:0.8;padding: 0 20px 20px 20px',
                  radioButtons(
                    inputId=ns('orig_dest_switch'),
                    h5("Change origin or destination"),
                    choices = c("Origin" = 'orig', "Destination" = 'dest'),
                    inline = TRUE),
                  actionButton(inputId=ns("confirm_itin"), label="Plot itinerary")
    )
  )
  
}

#' itinerary Server Functions
#'
#' @noRd 
mod_itinerary_server <- function(id, r, map_id, parent_session){
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    # Change origin/destination switch
    observeEvent(input$orig_dest_switch, {
      r$orig_dest_switch = input$orig_dest_switch
    })
    
    # Click on confirm button
    observeEvent(input$confirm_itin, {
    # Delete previous itineraries
    leaflet::leafletProxy(mapId = map_id, session = parent_session) %>%
      leaflet::clearGroup('itin')
    # Show itinerary on map
    leaflet::leafletProxy(mapId = map_id, session = parent_session) %>%
      leaflet::addPolylines(lng=c(r$origin$lng, r$destination$lng),
                            lat=c(r$origin$lat, r$destination$lat),
                            group='itin')
        
    })
    
    
  })
}
shinyApp(app_ui, app_server)

【问题讨论】:

  • segments 没有在 mod_itinerary.R 的 for 循环中定义,我认为这就是为什么你有错误“参数长度为零”
  • 对,对不起。这是较长代码的残余,我为这篇文章简化了它,我把它去掉了,谢谢你的评论。

标签: r shiny leaflet golem


【解决方案1】:

我不知道它为什么会崩溃,我没有这个问题。尽管如此,主要问题还是在app_server.R 中,当您调用模块进行行程时,您输入了map_id="basemap"

mod_basemap.R中,地图确实叫“basemap”,但是它被包裹在ns()中,也就是说它的实际id是“你调用模块时给的名字-”+“你给模块的id”输入”。因此,此处地图的实际 ID 为“basemap_ui_1-basemap”。

现在,指定整个 id 不是一个好主意(如果你以后用其他东西替换“basemap_ui_1”怎么办?),所以你想要在调用mod_basemap.R 时返回地图 id,这样你可以在其他模块中使用这个id。所以在mod_basemap.R的server部分的最后,可以添加:

return(list(map_id = ns("basemap")))

这样你就有了:

mod_basemap_server <- function(id, r, session){
  shiny::moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    # ... Code you already have ...

    return(list(map_id = ns("basemap")))
    
  })
}

app_server.R 中,您现在可以将模块mod_basemap 分配给一个对象(我称之为basemap),然后在您调用模块mod_itinerary 时使用basemap$map_id

basemap <- mod_basemap_server("basemap_ui_1", r, session)
mod_itinerary_server("itinerary_ui_1", r, map_id = basemap$map_id, parent_session = session)

我希望这很清楚。您也可以查看this RStudio article 了解模块之间的信息传递。

最后一件事,你在mod_itinerary.R 中打错字了,你写了r$destination$lng 应该是r$destination$lat

这样,单击地图上的两个点,然后单击“绘制路线”应该会在这两个点之间显示一条线。如果您仍然有一些意外的崩溃,我猜这来自您可能拥有的其他模块中的代码,因为该代码对我来说很好。

【讨论】:

  • 真棒@bretauv,非常感谢!我确实纠正了我在 OP 中的错字。我尝试了几次来了解该行return(list(map_id = ns("basemap"))) 的位置,然后它工作正常。我只需要在我更大的代码中实现它。
  • 好的,我将编辑我的答案以使其更清晰。如果它解决了您的问题,请不要忘记验证答案。
猜你喜欢
  • 2016-08-28
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-18
  • 2019-06-14
  • 1970-01-01
相关资源
最近更新 更多