Which is the best way to work with two different parameters bound to the legend?

Hi everyone, I'm reposting this from this discussion in vega-lite's github in case someone can help me.

I have a line chart and I want to group the lines using different fields. I've achieved this using a selection parameter.

Here's the example definition:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "params": [
    {
      "name": "dimention",
      "value": "dimention1",
      "bind": {"input": "select", "options": ["dimention1", "dimention2"]}
    },
    {
      "name": "selection",
      "select": {"type": "point", "fields": ["group"], "toggle": "true"},
      "bind": "legend"
    }
  ],
  "data": {
    "values": [
      {
        "dimention1": 1,
        "dimention2": "C",
        "data": [
          {"x": 1, "y": 1},
          {"x": 2, "y": 1},
          {"x": 3, "y": 1},
          {"x": 4, "y": 1},
          {"x": 5, "y": 1}
        ]
      },
      {
        "dimention1": 2,
        "dimention2": "B",
        "data": [
          {"x": 1, "y": 2},
          {"x": 2, "y": 2},
          {"x": 3, "y": 2},
          {"x": 4, "y": 2},
          {"x": 5, "y": 2}
        ]
      },
      {
        "dimention1": 3,
        "dimention2": "A",
        "data": [
          {"x": 1, "y": 3},
          {"x": 2, "y": 3},
          {"x": 3, "y": 3},
          {"x": 4, "y": 3},
          {"x": 5, "y": 3}
        ]
      }
    ]
  },
  "transform": [
    {"flatten": ["data"], "as": ["points"]},
    {"calculate": "datum[dimention]", "as": "group"}
  ],
  "mark": "line",
  "encoding": {
    "x": {"field": "points.x", "type": "quantitative"},
    "y": {"field": "points.y", "type": "quantitative"},
    "color": {"field": "group"},
    "opacity": {"condition": {"param": "selection", "value": 1}, "value": 0}
  }
}

If I select a group in the legend and then switch fields the chart goes blank. I understand that it's because the signal holds the values selected using the previous field and that I just need to select the groups in the new legend to show the lines, but I want to show all the lines (clear the values in the signal) when I change fields.

I thought about dividing the visualization in layers, create a param in each one to hold each legend values and set the whole visualization opacity to 0 if it's field is not selected, but the signals are bound to the same legend, so clicking on it adds or removes values from both signals. I tried setting "toggle": "dimention == 'dimentionX'" to just add values from their respective group, but toggle only works when working with multiple values, which is not quite what I need.

Is this the best way to handle this?
Would it be better to switch to full Vega?

Thanks!

I've managed to get something working with Vega, compiling your Vega-lite spec into Vega first:

npx -p vega -p vega-lite vl2vg vega-lite-spec.json

then I've added to any signal who listened the view-click event and additional dimention event to listen, and in that case return a null selection on update:

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "background": "white",
  "padding": 5,
  "width": 200,
  "height": 200,
  "style": "cell",
  "data": [
    {"name": "selection_store"},
    {
      "name": "source_0",
      "values": [
        {
          "dimention1": 1,
          "dimention2": "C",
          "data": [
            {"x": 1, "y": 1},
            {"x": 2, "y": 1},
            {"x": 3, "y": 1},
            {"x": 4, "y": 1},
            {"x": 5, "y": 1}
          ]
        },
        {
          "dimention1": 2,
          "dimention2": "B",
          "data": [
            {"x": 1, "y": 2},
            {"x": 2, "y": 2},
            {"x": 3, "y": 2},
            {"x": 4, "y": 2},
            {"x": 5, "y": 2}
          ]
        },
        {
          "dimention1": 3,
          "dimention2": "A",
          "data": [
            {"x": 1, "y": 3},
            {"x": 2, "y": 3},
            {"x": 3, "y": 3},
            {"x": 4, "y": 3},
            {"x": 5, "y": 3}
          ]
        }
      ]
    },
    {
      "name": "data_0",
      "source": "source_0",
      "transform": [
        {"type": "flatten", "fields": ["data"], "as": ["points"]},
        {
          "type": "formula",
          "expr": "datum[\"points\"] && datum[\"points\"][\"x\"]",
          "as": "points.x"
        },
        {
          "type": "formula",
          "expr": "datum[\"points\"] && datum[\"points\"][\"y\"]",
          "as": "points.y"
        },
        {"type": "formula", "expr": "datum[dimention]", "as": "group"}
      ]
    }
  ],
  "signals": [
    {
      "name": "unit",
      "value": {},
      "on": [
        {"events": "mousemove", "update": "isTuple(group()) ? group() : unit"}
      ]
    },
    {
      "name": "selection_group_legend",
      "value": null,
      "on": [
        {
          "events": [
            {
              "source": "view",
              "type": "click",
              "markname": "group_legend_symbols"
            },
            {
              "source": "view",
              "type": "click",
              "markname": "group_legend_labels"
            },
            {
              "source": "view",
              "type": "click",
              "markname": "group_legend_entries"
            }
          ],
          "update": "isDefined(datum.value) ? datum.value : item().items[0].items[0].datum.value",
          "force": true
        },
        {
          "events": [{"source": "view", "type": "click"}],
          "update": "!event.item || !datum ? null : selection_group_legend",
          "force": true
        },
        {
          "events": [{"signal": "dimention"}],
          "update": "null",
          "force": true
        }
      ]
    },
    {
      "name": "selection",
      "update": "vlSelectionResolve(\"selection_store\", \"union\", true, true)"
    },
    {
      "name": "dimention",
      "value": "dimention1",
      "bind": {"input": "select", "options": ["dimention1", "dimention2"]}
    },
    {
      "name": "selection_tuple",
      "update": "selection_group_legend !== null ? {fields: selection_tuple_fields, values: [selection_group_legend]} : null"
    },
    {
      "name": "selection_tuple_fields",
      "value": [{"type": "E", "field": "group"}]
    },
    {
      "name": "selection_toggle",
      "value": false,
      "on": [
        {
          "events": {"merge": [{"source": "view", "type": "click"}]},
          "update": "true"
        },
        {
          "events": [{"signal": "dimention"}],
          "update": "null",
          "force": true
        }
      ]
    },
    {
      "name": "selection_modify",
      "on": [
        {
          "events": {"signal": "selection_tuple"},
          "update": "modify(\"selection_store\", selection_toggle ? null : selection_tuple, selection_toggle ? null : true, selection_toggle ? selection_tuple : null)"
        }
      ]
    }
  ],
  "marks": [
    {
      "name": "pathgroup",
      "type": "group",
      "from": {
        "facet": {
          "name": "faceted_path_main",
          "data": "data_0",
          "groupby": ["group"]
        }
      },
      "encode": {
        "update": {
          "width": {"field": {"group": "width"}},
          "height": {"field": {"group": "height"}}
        }
      },
      "marks": [
        {
          "name": "marks",
          "type": "line",
          "style": ["line"],
          "sort": {"field": "datum[\"points.x\"]"},
          "interactive": true,
          "from": {"data": "faceted_path_main"},
          "encode": {
            "update": {
              "stroke": {"scale": "color", "field": "group"},
              "opacity": [
                {
                  "test": "!length(data(\"selection_store\")) || vlSelectionTest(\"selection_store\", datum)",
                  "value": 1
                },
                {"value": 0}
              ],
              "description": {
                "signal": "\"points.x: \" + (format(datum[\"points.x\"], \"\")) + \"; points.y: \" + (format(datum[\"points.y\"], \"\")) + \"; group: \" + (isValid(datum[\"group\"]) ? datum[\"group\"] : \"\"+datum[\"group\"])"
              },
              "x": {"scale": "x", "field": "points\\.x"},
              "y": {"scale": "y", "field": "points\\.y"},
              "defined": {
                "signal": "isValid(datum[\"points.x\"]) && isFinite(+datum[\"points.x\"]) && isValid(datum[\"points.y\"]) && isFinite(+datum[\"points.y\"])"
              }
            }
          }
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "linear",
      "domain": {"data": "data_0", "field": "points\\.x"},
      "range": [0, {"signal": "width"}],
      "nice": true,
      "zero": false
    },
    {
      "name": "y",
      "type": "linear",
      "domain": {"data": "data_0", "field": "points\\.y"},
      "range": [{"signal": "height"}, 0],
      "nice": true,
      "zero": true
    },
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "data_0", "field": "group", "sort": true},
      "range": "category"
    }
  ],
  "axes": [
    {
      "scale": "x",
      "orient": "bottom",
      "gridScale": "y",
      "grid": true,
      "tickCount": {"signal": "ceil(width/40)"},
      "domain": false,
      "labels": false,
      "aria": false,
      "maxExtent": 0,
      "minExtent": 0,
      "ticks": false,
      "zindex": 0
    },
    {
      "scale": "y",
      "orient": "left",
      "gridScale": "x",
      "grid": true,
      "tickCount": {"signal": "ceil(height/40)"},
      "domain": false,
      "labels": false,
      "aria": false,
      "maxExtent": 0,
      "minExtent": 0,
      "ticks": false,
      "zindex": 0
    },
    {
      "scale": "x",
      "orient": "bottom",
      "grid": false,
      "title": "points.x",
      "labelFlush": true,
      "labelOverlap": true,
      "tickCount": {"signal": "ceil(width/40)"},
      "zindex": 0
    },
    {
      "scale": "y",
      "orient": "left",
      "grid": false,
      "title": "points.y",
      "labelOverlap": true,
      "tickCount": {"signal": "ceil(height/40)"},
      "zindex": 0
    }
  ],
  "legends": [
    {
      "stroke": "color",
      "symbolType": "stroke",
      "title": "group",
      "encode": {
        "labels": {
          "name": "group_legend_labels",
          "interactive": true,
          "update": {
            "opacity": [
              {
                "test": "(!length(data(\"selection_store\")) || (selection[\"group\"] && indexof(selection[\"group\"], datum.value) >= 0))",
                "value": 1
              },
              {"value": 0.35}
            ]
          }
        },
        "symbols": {
          "name": "group_legend_symbols",
          "interactive": true,
          "update": {
            "opacity": [
              {
                "test": "(!length(data(\"selection_store\")) || (selection[\"group\"] && indexof(selection[\"group\"], datum.value) >= 0))",
                "value": 1
              },
              {"value": 0.35}
            ]
          }
        },
        "entries": {
          "name": "group_legend_entries",
          "interactive": true,
          "update": {"fill": {"value": "transparent"}}
        }
      }
    }
  ]
}
2 Likes

Thank you! This definitely worked!

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.