使用 Leaflet/Shiny 选择和取消选择多个多边形时更改样式 [英] Changing styles when selecting and deselecting multiple polygons with Leaflet/Shiny

查看:20
本文介绍了使用 Leaflet/Shiny 选择和取消选择多个多边形时更改样式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我正在开发的 Leaflet Shiny 应用程序中选择和取消选择多边形时,我在更改多边形样式时遇到了一些问题.在我当前的应用程序中,当您单击一个多边形时,该多边形会以不同的颜色突出显示.理想情况下,我希望用户能够选择并突出显示多个多边形.我还希望用户能够重新单击单个突出显示的多边形以取消选择它.

我能够管理的最好方法是选择多个多边形,为它们提供已选择"的相同组 ID,然后在重新单击多边形时取消选择整个组.这是一些示例/可重现的代码:

库(光栅)图书馆(闪亮)图书馆(传单)#加载形状文件rwa <- getData("GADM", country = "RWA", level = 1)闪亮的应用程序(ui =流体页面(传单输出(地图")),服务器 <- 功能(输入,输出,会话){#初始地图输出输出$map <- renderLeaflet({传单() %>%addTiles() %>%addPolygons(数据 = rwa,fillColor = "白色",填充不透明度 = 1,颜色=黑色",行程 = T,重量 = 1,layerId = rwa@data$OBJECTID,组=地区")}) #END 渲染传单观察事件(输入$map_shape_click,{#为单击的多边形创建对象点击 <- 输入$map_shape_click#define 二级区域地图的传单代理代理 <- 传单代理(地图")#subset 区域 shapefile 通过单击的多边形selectedReg <-rwa[rwa@data$OBJECTID == click$id,]#map 点击多边形代理 %>% addPolygons(data = selectedReg,fillColor = "红色",填充不透明度 = 1,重量 = 1,颜色=黑色",行程 = T,组=选择",# layerId = "选中")layerId = selectedReg@data$OBJECTID)#remove 被点击两次的多边形组如果(点击$组==选择"){代理 %>%清除组(组=选择")} #END 条件}) #END OBSERVE 事件}) #END SHINYAPP

在上面的例子中,每个被点击的多边形都会变成红色.如果再次单击先前选择的红色多边形,则将从地图中清除每个红色多边形,留下初始的白色多边形渲染.

当我一次只使用一个多边形时,我可以通过使用字符串 layerId selected"(在上面的代码中注释掉)来实现所需的选择/取消选择效果,但是这样做会消除我选择和取消选择的能力同时突出显示多个多边形.

我愿意接受任何和所有建议!

解决方案

答案就在layerIds中.我不明白这些是如何应用于我的多边形和删除形状的——理解这是关键.这可能不是最优雅的解决方案,但它可以完成工作!

在下面的代码中,卢旺达的初始地图渲染有一个layerIdrwa@data$NAME_1,即区域名称.您可以在 label 也设置为 rwa@data$NAME_1 的情况下看到这一点.所以在下图中,最左边的多边形被标记为 Iburengerazuba,它的属性在 NAME_1 列中.此 layerId 为您在此初始地图渲染中的任何点击事件设置 click$id. 因此,正如这个多边形被标记为 Iburengerazuba,它的 click$id 也将设置为 Iburengerazuba.

接下来是形状点击的 observeEvent.

移除红色点击多边形的关键是为这些多边形赋予一个不同于初始地图​​渲染的 layerId. 请注意,在上图中,白色多边形是标记为 Iburengerazuba 现在标记为 3.这是因为第二个 addPolygons 调用中的 layerId 设置为 CC_1 INSTEAD OF NAME_1.因此,底层白色地图有一个 NAME_1 图层 ID,因此有 NAME_1 点击 ID,而绘制在其顶部的任何红色点击多边形都有一个 CC_1 图层 ID,因此有 CC_1 点击 ID.

if 语句声明如果您的 click$id 已存在于 clickedPolys 多边形中,则该形状将被移除.这有点令人困惑,所以再一次,它可能有助于遍历每一行代码并玩弄它以真正理解.

再次使用上面的示例,单击最左侧的多边形会将 layerId Iburengerazuba 添加到 clickedIds$ids 向量中.此单击事件触发第二次地图绘制,以不同的样式在其自身顶部绘制单击的多边形,并且 layerId 为 3(来自 CC_1 列).我们想说,如果任何红色多边形被点击两次(if(click$id %in% clickedPolys@data$CC_1)),它就被视为取消选择,并且应该从地图.因此,如果您单击 layerId 为 3 的最左侧红色多边形,则 clickedIds$ids 向量将由 Iburengerazuba 组成3.clickedPolys 多边形的 NAME_1 列中的 Iburengerazuba 对应 CC_1 列中的 3,触发 if 语句.调用 removeShape(layerId = click$id) 意味着删除对应于该 click$id 的形状.所以在这种情况下,clickedPolys 多边形的 CC_1 layerId 为 3.

请记住,每次点击 id,NAME_1CC_1 都记录在您的 clickedIds$ids 矢量.该向量将您的卢旺达 shapefile 设置为子集以映射所有单击的多边形,因此当您单击多边形时,clickedPolys 多边形正在动态更新(使用 print 调用来检查如果这对您没有意义,请编写代码!).删除任何双击的形状并不足以正确绘制所有内容 --您需要从 clickedIds$ids 向量 中删除取消选择的 layerId,包括 NAME_1 和 CC_1.我将每个取消选择的 CC_1 layerId 与其对应的 NAME_1 值匹配,并从 clickedIds$ids 向量中删除了这两个属性,以便将它们从clickedPolys 多边形.

瞧!现在您可以选择和取消选择您想要的任何多边形!

库(光栅)图书馆(闪亮)图书馆(传单)#加载形状文件rwa <- getData(GADM",国家 = RWA",级别 = 1)闪亮的应用程序(ui =流体页面(传单输出(地图")),服务器 <- 功能(输入,输出,会话){#创建空向量来保存所有点击IDclickedIds <-reactiveValues(ids = vector())#初始地图输出输出$map <- renderLeaflet({传单() %>%addTiles() %>%addPolygons(数据 = rwa,fillColor =白色",填充不透明度 = 1,颜色=黑色",行程 = T,重量 = 1,layerId = rwa@data$NAME_1,组=区域",标签 = rwa@data$NAME_1)}) #END 渲染传单观察事件(输入$map_shape_click,{#为单击的多边形创建对象点击 <- 输入$map_shape_click#define 二级区域地图的传单代理proxy <-leafletProxy(map")#将所有点击ID添加到空向量中clickedIds$ids <- c(clickedIds$ids, click$id)#shapefile 与所有单击的多边形 - 原始 shapefile 由单击列表中的所有管理员名称子集clickedPolys <- rwa[rwa@data$NAME_1 %in% clickedIds$ids, ]#如果当前点击ID [来自CC_1] 存在于点击的多边形中(如果它被点击了两次)if(click$id %in% clickedPolys@data$CC_1){#define 将与 CC_1 点击 ID 匹配的 NAME 子集的向量nameMatch <- clickedPolys@data$NAME_1[clickedPolys@data$CC_1 == click$id]#从 clickedPolys shapefile 中删除当前 click$id 及其名称匹配clickedIds$ids <- clickedIds$ids[!clickedIds$ids %in% click$id]clickedIds$ids <- clickedIds$ids[!clickedIds$ids %in% nameMatch]#从地图中删除突出显示的多边形代理 %>% removeShape(layerId = click$id)} 别的 {#map 突出显示的多边形代理 %>% addPolygons(data = clickedPolys,填充颜​​色=红色",填充不透明度 = 1,重量 = 1,颜色=黑色",行程 = T,标签 = clickedPolys@data$CC_1,layerId = clickedPolys@data$CC_1)} #END 条件}) #END OBSERVE 事件}) #END SHINYAPP

I'm having some problems changing polygon styles when selecting and deselecting polygons in a Leaflet Shiny app I'm working on. In my current app, when you click on a polygon, that polygon is highlighted with a different color. Ideally, I want the user to be able to select and highlight multiple polygons. I also want the user to be able to re-click a single highlighted polygon to deselect it.

The best that I've been able to manage is to select multiple polygons, give them the same group ID "selected", then deselect that entire group when a polygon is re-clicked. Here's some example/reproducible code:

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

#load shapefile
rwa <- getData("GADM", country = "RWA", level = 1)

shinyApp(
  ui = fluidPage(
    leafletOutput("map")
  ), 

  server <- function(input, output, session){

    #initial map output
    output$map <- renderLeaflet({
      leaflet() %>% 
        addTiles() %>% 
        addPolygons(data = rwa, 
                    fillColor = "white", 
                    fillOpacity = 1, 
                    color = "black", 
                    stroke = T, 
                    weight = 1, 
                    layerId = rwa@data$OBJECTID, 
                    group = "regions")
    }) #END RENDER LEAFLET

    observeEvent(input$map_shape_click, {

      #create object for clicked polygon
      click <- input$map_shape_click

      #define leaflet proxy for second regional level map
      proxy <- leafletProxy("map")

      #subset regions shapefile by the clicked on polygons
      selectedReg <-rwa[rwa@data$OBJECTID == click$id,]


      #map clicked on polygons
      proxy %>% addPolygons(data = selectedReg,
                            fillColor = "red",
                            fillOpacity = 1,
                            weight = 1,
                            color = "black", 
                            stroke = T,
                            group = "selected",
                            # layerId = "selected")
                            layerId = selectedReg@data$OBJECTID)


      #remove polygon group that are clicked twice 
      if(click$group == "selected"){
        proxy %>% 
          clearGroup(group = "selected")
      } #END CONDITIONAL 

    }) #END OBSERVE EVENT

  }) #END SHINYAPP

In the above example, every clicked polygon turns red. If a previously-selected red polygon is clicked again, every red polygon is cleared from the map, leaving the initial white polygon renderings.

I can accomplish the desired selecting/deselecting effect when I'm working with only one polygon at a time by using the string layerId "selected" (commented out in the above code), but doing that removes my ability to select and highlight multiple polygons at the same time.

I'm open to any and all suggestions!

解决方案

The answer lies in layerIds. I wasn't understanding how these were applied to my polygons and removing shapes--understanding this is key. This might not be the most elegant solution, but it gets the job done!

In the below code, the initial map rendering of Rwanda has a layerId of rwa@data$NAME_1, which are the region names. You can see this in action with the label also being set as rwa@data$NAME_1. So in the below image, the leftmost polygon is labeled as Iburengerazuba, its attribute in the NAME_1 column. This layerId sets the click$id for any click events you have on this initial map rendering. So, just as this polygon is labeled Iburengerazuba, its click$id will also be set as Iburengerazuba. As stated in the Leaflet Shiny documentation, if you've got more than one polygon, this needs to be a vectorized argument. If you only need to select and deselect ONE polygon (so only one region at a time, in this example), you could use a layerId string, as I mentioned in my question (such as layerId = "selected").

Next up is the observeEvent for your shape click. Thanks to the help of user @John Paul, I figured out how to save all click events (click ids specifically in this case) made on the map. I saved those in a reactive vector, then subset my shapefile by those click ids. The code is pretty thoroughly commented, so hopefully anyone else looking for this same solution can figure out exactly what's going on.

The final bit of code (housed in the if...else conditional statement) is probably the most confusing. Let's look at the else portion of the code first. (Note: Your initial map click is going to trigger this event because there's no way for the if conditions to have been met upon first click.) If any white polygon is clicked, the addPolygons() call is triggered, adding the clicked polygon onto the map with different styling (in this case, it's red). This is plotting an entirely different polygon on top of the leafletProxy object!

The key to removing the red clicked polygons is giving these polygons a different layerId than the initial map rendering. Note that in the above image, the white polygon that was labeled Iburengerazuba is now labeled as 3. This is because the layerId in the second addPolygons call is set as CC_1 INSTEAD OF NAME_1. So, bottom layer white map has a NAME_1 layerID and therefore NAME_1 click ids, whereas any red clicked polygon plotted on top of that has a CC_1 layerId and therefore CC_1 click ids.

The if statements states that if your click$id already exists in the clickedPolys polygon, that this shape is removed. This is kind of confusing, so again, it might help to go through each line of code and play around with it to truly understand.

Again using the above example, clicking the leftmost polygon adds the layerId Iburengerazuba to the clickedIds$ids vector. This click event triggers a second map drawing, plotting the clicked polygon on top of itself in a different style and with a layerId of 3 (from the CC_1 column). We want to say that if any red polygon is clicked twice (if(click$id %in% clickedPolys@data$CC_1)), it counts as a deselection, and that polygon should be removed from the map. So if you click on the red leftmost polygon with a layerId of 3, the clickedIds$ids vector will be comprised of Iburengerazuba and 3. Iburengerazuba in the NAME_1 column of the clickedPolys polygon corresponds to 3 in the CC_1 column, triggering the if statement. The call removeShape(layerId = click$id) means to remove the shape that corresponds to that click$id. So in this case, the clickedPolys polygon with a CC_1 layerId of 3.

Keep in mind that every click id, both NAME_1 and CC_1 are being recorded in your clickedIds$ids vector. This vector is subsetting your Rwanda shapefile to map all clicked polygons, so as you're clicking polygons, the clickedPolys polygon is dynamically updating (use print calls to check every bit of code if this isn't making sense to you!). Removing any double-clicked shape isn't enough to plot everything correctly--you need to remove deselected layerIds, both NAME_1 and CC_1, from the clickedIds$ids vector. I matched each deselected CC_1 layerId to its corresponding NAME_1 value and removed both of those attributes from the clickedIds$ids vector so that they are removed from the clickedPolys polygon.

Voila! Now you can select and deselect any polygons you want!

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

#load shapefile
rwa <- getData("GADM", country = "RWA", level = 1)

shinyApp(
  ui = fluidPage(
    leafletOutput("map")
  ), 
  
  server <- function(input, output, session){
    
    #create empty vector to hold all click ids
    clickedIds <- reactiveValues(ids = vector())
    
    #initial map output
    output$map <- renderLeaflet({
      leaflet() %>% 
        addTiles() %>% 
        addPolygons(data = rwa, 
                    fillColor = "white", 
                    fillOpacity = 1, 
                    color = "black", 
                    stroke = T, 
                    weight = 1, 
                    layerId = rwa@data$NAME_1, 
                    group = "regions", 
                    label = rwa@data$NAME_1)
    }) #END RENDER LEAFLET
    
    observeEvent(input$map_shape_click, {
      
      #create object for clicked polygon
      click <- input$map_shape_click
      
      #define leaflet proxy for second regional level map
      proxy <- leafletProxy("map")
      
      #append all click ids in empty vector 
      clickedIds$ids <- c(clickedIds$ids, click$id)
      
      #shapefile with all clicked polygons - original shapefile subsetted by all admin names from the click list
      clickedPolys <- rwa[rwa@data$NAME_1 %in% clickedIds$ids, ]
      
      #if the current click ID [from CC_1] exists in the clicked polygon (if it has been clicked twice)
      if(click$id %in% clickedPolys@data$CC_1){
        
        #define vector that subsets NAME that matches CC_1 click ID
        nameMatch <- clickedPolys@data$NAME_1[clickedPolys@data$CC_1 == click$id]
        
        #remove the current click$id AND its name match from the clickedPolys shapefile
        clickedIds$ids <- clickedIds$ids[!clickedIds$ids %in% click$id] 
        clickedIds$ids <- clickedIds$ids[!clickedIds$ids %in% nameMatch]
        
        #remove that highlighted polygon from the map
        proxy %>% removeShape(layerId = click$id)
        
      } else {
        
        #map highlighted polygons
        proxy %>% addPolygons(data = clickedPolys,
                              fillColor = "red",
                              fillOpacity = 1,
                              weight = 1,
                              color = "black",
                              stroke = T,
                              label = clickedPolys@data$CC_1, 
                              layerId = clickedPolys@data$CC_1)
      } #END CONDITIONAL
    }) #END OBSERVE EVENT
  }) #END SHINYAPP

这篇关于使用 Leaflet/Shiny 选择和取消选择多个多边形时更改样式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆