使用Leaflet/Shiny选择和取消选择多个多边形时更改样式 [英] Changing styles when selecting and deselecting multiple polygons with 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!
在下面的代码中,卢旺达的初始地图呈现的layerId
为rwa@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
矢量将由Iburengerazuba
和3
组成. 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_1
和CCA_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屋!