如何防止传单地图在 R/shiny 中发生双击事件时放大

How to prevent a leaflet map from zooming in when a double-click event occurs in R/shiny

提问人:Faustin Gashakamba 提问时间:11/16/2023 更新时间:11/16/2023 访问量:19

问:

我正在 UI 的此输出小部件中以 R/shiny 渲染地图。 在服务器中,我使用创建地图对象,然后在我使用中显示地图。leafletOutput("map")tmaprenderLeaflet()tmap_leaflet(tmap_object)

我想单击地图上的要素,捕获单击的坐标,以便选择单击的要素。我会使用此信息在另一个小部件中显示表格。 这是我的完整服务器功能:

server <- function(input, output, session) {
  # get user input
  my_season <- reactive(input$my_season)
  statistic <- reactive(input$statistic)
  
  # filter the data using user input
  my_data <- reactive({
    data_filtered <- data %>% filter(season == my_season()) 
    return(data_filtered)
  })

  # display map
  output$map <- renderLeaflet({
    # create the tmap object
    tmap_object <- tm_shape(my_data()) + tm_borders() +
      tm_fill(col = statistic(),
              title = str_to_sentence(str_replace_all(statistic(), "_", " ")),
              style = "jenks", 
              palette = viridis(7),
              id = "sector", 
              alpha = .6) +
      tm_shape(country) + tm_borders(lwd = 2) +
      tm_view(bbox = st_bbox(country))  # Set the initial extent to fit the "country" layer
    
    # convert the tmap object to a leaflet map 
    tmap_leaflet(tmap_object) 
  })
  
  # Create a reactive value to store the clicked coordinates
  clicked_coords <- reactiveValues(lon = NA, lat = NA)
  
  # Observe the map_click event and get coordinates of the clicked spot
  observeEvent(input$map_click, {
    clicked_coords$lon <- input$map_click$lng
    clicked_coords$lat <- input$map_click$lat
  })

  # display the summary of the clicked territory in a table
  output$details <- renderTable({
    # Filter the data given the area clicked 
    longitude <- as.numeric(clicked_coords$lon)
    latitude <- as.numeric(clicked_coords$lat)
    clicked_spot <- st_sfc(st_point(c(longitude, latitude)), crs=4326)  # Create an sf point
    clicked_spot <- st_transform(clicked_spot, crs = st_crs(my_data())) # Re-project the point appropriately
    clicked_district <- my_data() %>% st_filter(clicked_spot, .predicate = st_contains) %>%
      st_drop_geometry() %>% pull(district)
    my_table <- my_data() %>% st_drop_geometry() %>%
      filter(district == clicked_district)
  }) 
}

挑战如下:

  1. 当我在地图上单击一次时,只显示弹出窗口,我的表没有更新。
  2. 当我双击地图时,我的表格会更新,但地图会同时放大。

我能否至少阻止 1 或 2(即跳过弹出窗口并将单击注册为将更新我的表的正确鼠标单击事件;或阻止双击放大我的地图)。

闪亮 的 R-Leaflet TMAP

评论

0赞 mnist 11/27/2023
尽管共享代码是件好事,但如果提供最小可重现的示例,则会增加获得答案的可能性。否则,谁想回答你的问题,就必须自己做。特别是对于闪亮的功能,这是一个更大的负担

答:

0赞 sonshine 12/15/2023 #1

我不完全确定这是否是你所追求的,但只需单击一下即可为我工作。我认为有更好的方法可以实现这一目标(和检查),但它在我的脑海中有效且有意义。reactive()NULL

我还建议使用可能不是您想要的,因为它会在每次单击时切换表格,而不管单击是否与数据相交。 即,我可以单击同一位置两次,它会显示表格,然后隐藏它,我不确定这是用户所期望的。您也许可以使用显示/隐藏按钮来代替?shinyjs::toggle()

此代码还添加了一个标记,用户单击该标记,我发现这很有用。无论如何,希望这会有所帮助!

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

# data gen ----

# outline
outline <- system.file("gpkg/nc.gpkg", package = "sf") |>
    sf::st_read() |>
    sf::st_geometry() |>
    sf::st_union() |>
    sf::st_as_sf() |>
    sf::st_transform("+proj=longlat +datum=WGS84")

# gen some polygons
poly <- outline |>
    sf::st_sample(40) |>
    sf::st_buffer(units::as_units(10, "mi")) |>
    sf::st_as_sf() |>
    dplyr::mutate(id = seq_len(dplyr::n())) |>
    dplyr::mutate(color = viridisLite::viridis(dplyr::n())) |>
    dplyr::rowwise() |>
    dplyr::mutate(label = htmltools::HTML(
        paste0(
            "<b>ID:</b> ", id, " <br/>",
            "<b>Color:</b> ", color
        )
    )) |>
    dplyr::ungroup() |>
    sf::st_set_agr("constant")

# ui ----

ui <- shiny::fluidPage(
    leaflet::leafletOutput("map"),
    shiny::tableOutput("tab")
)

# server ----

server <- function(input, output, session) {
    # initial map
    output$map <- leaflet::renderLeaflet({
        leaflet::leaflet() |>
            leaflet::addTiles(group = "OpenStreetMap") |>
            leaflet::addPolygons(
                data = outline,
                fillOpacity = .15,
                fillColor = "black",
                color = "black", weight = 2
            ) |>
            leaflet::addPolygons(
                data = poly,
                fillColor = ~color,
                fillOpacity = .75,
                stroke = FALSE,
                label = ~label
            )
    })

    # click logic
    observe({
        # clear previous click
        leaflet::leafletProxy("map") |>
            leaflet::clearMarkers()

        # get new click
        click <- input$map_click

        # add click to map
        leaflet::leafletProxy("map") |>
            leaflet::addMarkers(lng = click$lng, lat = click$lat)
    }) |>
        shiny::bindEvent(input$map_click)

    # pt
    pt <- shiny::reactive({
        if (!is.null(input$map_click$lat)) {
            pt <- sf::st_point(
                c(
                    input$map_click$lng,
                    input$map_click$lat
                )
            ) |>
                sf::st_sfc(crs = 4326) |>
                sf::st_as_sf()

            return(pt)
        } else {
            NULL
        }
    })

    # intersects
    intersects <- shiny::reactive({
        if (!is.null(pt())) {
            sf::st_intersection(pt(), poly) |>
                sf::st_drop_geometry() |>
                dplyr::select(-label)
        } else {
            NULL
        }
    })

    # table
    output$tab <- shiny::renderTable({
        if (is.null(intersects())) {
            return(NULL)
        }
        if (nrow(intersects()) > 0) {
            intersects()
        } else {
            NULL
        }
    })
}

评论

0赞 Faustin Gashakamba 12/15/2023
我很欣赏在用户单击的地方添加一个标记。但是,我最初的问题都没有得到解决。我仍然必须单击一次以显示弹出窗口,然后单击第二次以显示表格。真的不是完全禁用弹出窗口的方法吗?
0赞 sonshine 12/16/2023
@FaustinGashakamba啊,我想我明白你在说什么。我建议使用基础传单而不是 tmap 来渲染地图,这样您就可以控制这些行为。我已经编辑了解决方案以包含一个标签(当您将鼠标悬停在标签上时,它会显示信息,而不必单击该点)。然后,您仍然可以单击一次并在应用中呈现表格。有关传单中的弹出窗口与标签的更多信息,请参阅 https://rstudio.github.io/leaflet/popups.html