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

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

问题描述

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

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.

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

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.

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

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!

推荐答案

答案在于layerIds.我当时还不了解如何将这些元素应用于多边形并删除形状-但这是关键.这可能不是最优雅的解决方案,但可以完成工作!

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!

在下面的代码中,卢旺达的初始地图呈现的layerIdrwa@data$NAME_1,这是区域名称.您还可以看到将label设置为rwa@data$NAME_1的情况.因此,在下图中,最左侧的多边形被标记为Iburengerazuba,其属性在NAME_1列中. 此layerId设置您在此初始地图呈现中发生的任何单击事件的click$id..因此,就像该多边形被标记为Iburengerazuba一样,其click$id也将被设置为Iburengerazuba. 如Leaflet Shiny文档中所述,如果您有多个多边形,则必须是向量化的参数.如果只需要选择和取消选择一个多边形(在此示例中一次只能选择一个区域),则可以使用layerId字符串,就像我在问题中提到的那样(例如layerId = "selected").

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").

下一步是点击形状的observeEvent. 感谢用户@John Paul ,我想出了如何保存在地图上进行的所有点击事件(在这种情况下,尤其是点击ID).我将它们保存在反应矢量中,然后通过这些点击ID将我的shapefile子集.该代码已被完全注释掉,因此希望其他寻求相同解决方案的人可以确切地了解正在发生的事情.

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.

最后一小段代码(包含在if...else条件语句中)可能最令人困惑.首先让我们看一下代码的else部分. (注意:您最初的地图单击将触发此事件,因为第一次单击时无法满足if条件.)如果单击了任何白色多边形,则会触发addPolygons()调用,并添加单击的多边形以不同的样式显示在地图上(在本例中为红色). 这是在leafletProxy对象顶部绘制了一个完全不同的多边形!

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!

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

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 CCA_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 CCA_1 layerId and therefore CCA_1 click ids.

if语句指出,如果click$id已存在于clickedPolys多边形中,则将删除此形状.这有点令人困惑,因此再次使用它可能有助于遍历每一行代码,并对其进行处理以真正理解.

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.

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

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 CCA_1 column). We want to say that if any red polygon is clicked twice (if(click$id %in% clickedPolys@data$CCA_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 CCA_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 CCA_1 layerId of 3.

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

Keep in mind that every click id, both NAME_1 and CCA_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 CCA_1, from the clickedIds$ids vector. I matched each deselected CCA_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!现在,您可以选择和取消选择任何想要的多边形!

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 CCA_1] exists in the clicked polygon (if it has been clicked twice)
      if(click$id %in% clickedPolys@data$CCA_1){

        #define vector that subsets NAME that matches CCA_1 click ID
        nameMatch <- clickedPolys@data$NAME_1[clickedPolys@data$CCA_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$CCA_1, 
                              layerId = clickedPolys@data$CCA_1)
      } #END CONDITIONAL
    }) #END OBSERVE EVENT
  }) #END SHINYAPP

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

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