leaflet icon indicating copy to clipboard operation
leaflet copied to clipboard

Crosstalk selection fails with multiple groups

Open kent37 opened this issue 3 years ago • 6 comments

Crosstalk selection of objects in a layer fails when there are multiple groups. Here is a minimal example Rmd file:

---
title: Fiji earthquakes
output: html_document
---

```{r}
library(crosstalk)
library(leaflet)

# Wrap data frame in SharedData
sd <- SharedData$new(quakes[sample(nrow(quakes), 100),])

# Create a filter input
filter_slider("mag", "Magnitude", sd, column=~mag, step=0.1, width=250)

leaflet(sd) %>% 
  addTiles() %>% 
  addCircles(group='Test1', color='red') %>% 
  addCircles(group='Test2', color='blue') %>% 
  addLayersControl(baseGroups=c('Test1', 'Test2'))

```

If either addCircles call is commented out the selection works properly. With two groups, the selection fails.

In my real code, I am displaying two different attributes of the dataset in the two layers and wish the selection to work on both layers.

kent37 avatar Jun 30 '21 14:06 kent37

Same here. This seems not related to r/leaflet version

maxcolomb avatar Jul 07 '21 11:07 maxcolomb

The problem seems to be in LayerManager's handleFilter and handleSelection methods, which don't expect ctg[key] to contain multiple values.

Here is a failing example that uses both filtering and selection:

---
title: Fiji earthquakes
output: html_document
---

  ```{r echo=FALSE}
library(crosstalk)
library(leaflet)
library(DT)

# Wrap data frame in SharedData
sd <- SharedData$new(quakes[sample(nrow(quakes), 100),])

# Create a filter input
filter_slider("mag", "Magnitude", sd, column=~mag, step=0.1, width=250)

leaflet(sd) %>%
  addTiles() %>%
  addCircles(group='Test1', color='red') %>%
  addCircles(group='Test2', color='blue') %>%
  addLayersControl(baseGroups=c('Test1', 'Test2'))

datatable(sd, extensions="Scroller", style="bootstrap", class="compact", width="100%",
  options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))

```

Here is a patch to layer-manager.js that fixes the above example by iterating ctg[key] (in four places).

diff --git a/javascript/src/layer-manager.js b/javascript/src/layer-manager.js
index 4bed8f4..040308a 100644
--- a/javascript/src/layer-manager.js
+++ b/javascript/src/layer-manager.js
@@ -107,8 +107,11 @@ export default class LayerManager {
             let groupKeys = Object.keys(ctg);
             for (let i = 0; i < groupKeys.length; i++) {
               let key = groupKeys[i];
-              let layerInfo = this._byStamp[ctg[key]];
-              this._setVisibility(layerInfo, true);
+              let ctgs = ctg[key];
+              for (let i2 = 0; i2 < ctgs.length; i2++) {
+                let layerInfo = this._byStamp[ctgs[i2]];
+                this._setVisibility(layerInfo, true);
+              }
             }
           } else {
             let selectedKeys = {};
@@ -118,8 +121,11 @@ export default class LayerManager {
             let groupKeys = Object.keys(ctg);
             for (let i = 0; i < groupKeys.length; i++) {
               let key = groupKeys[i];
-              let layerInfo = this._byStamp[ctg[key]];
-              this._setVisibility(layerInfo, selectedKeys[groupKeys[i]]);
+              let ctgs = ctg[key];
+              for (let i2 = 0; i2 < ctgs.length; i2++) {
+                let layerInfo = this._byStamp[ctgs[i2]];
+                this._setVisibility(layerInfo, selectedKeys[groupKeys[i]]);
+              }
             }
           }
         };
@@ -131,8 +137,11 @@ export default class LayerManager {
             let groupKeys = Object.keys(ctg);
             for (let i = 0; i < groupKeys.length; i++) {
               let key = groupKeys[i];
-              let layerInfo = this._byStamp[ctg[key]];
-              this._setOpacity(layerInfo, 1.0);
+              let ctgs = ctg[key];
+              for (let i2 = 0; i2 < ctgs.length; i2++) {
+                let layerInfo = this._byStamp[ctgs[i2]];
+                this._setOpacity(layerInfo, 1.0);
+              }
             }
           } else {
             let selectedKeys = {};
@@ -142,8 +151,11 @@ export default class LayerManager {
             let groupKeys = Object.keys(ctg);
             for (let i = 0; i < groupKeys.length; i++) {
               let key = groupKeys[i];
-              let layerInfo = this._byStamp[ctg[key]];
-              this._setOpacity(layerInfo, selectedKeys[groupKeys[i]] ? 1.0 : 0.2);
+              let ctgs = ctg[key];
+              for (let i2 = 0; i2 < ctgs.length; i2++) {
+                let layerInfo = this._byStamp[ctgs[i2]];
+                this._setOpacity(layerInfo, selectedKeys[groupKeys[i]] ? 1.0 : 0.2);
+              }
             }
           }
         };

kent37 avatar Aug 23 '21 21:08 kent37

I am using rstudio and r-markdown. How is this patch applied?

riemerra avatar Dec 15 '22 01:12 riemerra

This is a patch to a file in the leaflet package. To apply it, make a local clone of the package, apply the patch, build and install your patched package.

kent37 avatar Dec 15 '22 15:12 kent37

Thanks. I will try to apply your explanation. However, I am a retired FORTRAN and SAS programmer who is just learning about r, JavaScript, and github. I am currently reading JavaScript for R by John Coene, 2021-04-19. So I was wondering if there is a development version of leaflet that I could install in rstudio that might already have this patch.

riemerra avatar Dec 15 '22 18:12 riemerra

Am I looking in the wrong place for layer-manager.js? https://cran.r-project.org/bin/windows/contrib/4.3/leaflet_2.1.1.zip does not appear to contain layer-manager.js. leaflet.js does have several references to layerManager.

riemerra avatar Dec 17 '22 02:12 riemerra